Inside the Contract-to-Invoice Engine: Escalation, Tiers, Usage, and Minimums
Gaurav Singhal
View LinkedInThe contract has the clause. Here's what actually computes it.
Last week we wrote about the gap between the CRM and the billing tool: the signed contract sits between them, and neither system reads it. When the contract is flat-fee, nobody notices, because duplicating last month's invoice and changing the date gets the job done. The moment a contract has a clause that a duplicate can't handle, a person in finance has to open Excel and compute the number by hand, because their job is on the line if it's wrong.
That post made the point with one example, a 12% escalation kicking in at year three. Contracts don't stop at escalation. Real B2B agreements mix flat fees, usage meters, volume slabs, minimum commitments, and free allowances, often inside the same contract. A billing engine that only handles the easy 80% just moves the Excel problem to the remaining 20%, which is usually where the money is.
Here's what our engine actually does with each of these, including the parts that are still incomplete.
Flat fee: clone, don't recompute
Flat-fee billing is the baseline, and treating it as "just duplicate last month" is mostly right, but not exactly right. Our CloneInvoice service takes a set of prior invoices and a target month, pulls the invoice and line-item rows, and rebuilds a new invoice by delegating back into the standard invoice-creation pipeline rather than writing a copy directly. That matters because it means a cloned invoice still runs through the same GST and rounding computation as a freshly generated one, instead of inheriting whatever numbers happened to be on last month's invoice.
One specific bug class we found and fixed: source invoices can carry a one-paisa drift in their stored totals, from rounding at the line-item level versus the invoice level. A naive clone copies that drift forward, and it compounds. Our clone step recomputes total_amount from the summed tax components at clone time rather than trusting the stored value. Every cloned invoice is created in draft status, with its invoice number set to a placeholder until a human approves it, which we'll come back to.
Escalation clauses: compounding, not linear, and it doesn't stop at rent
The LinkedIn post used a 12% escalation as the example that breaks duplication. In our system, escalation isn't a special case bolted onto flat billing, it's a first-class contract attribute: a contract line carries a price, a billing unit, an escalation start date, an escalation percentage, and an escalation frequency in months.
When those fields are set, the engine doesn't apply the percentage once and move on. It splits the contract term into successive sub-periods at the escalation frequency and compounds the price at each step: period i's rate is the base price times (1 + escalation)^(i-1), rounded to five decimal places, generated period by period until it reaches the contract's end date, at which point the final sub-period is truncated rather than overrun. A 12% escalation at year three isn't one adjustment, it's a full schedule of stepped rates that the invoice generator walks through automatically for the life of the contract.
This logic exists independently in three of our billing verticals: general contracts, commercial real estate leases, and one other product line, because each vertical grew its own contract and invoice generation pipeline. It's the same algorithm each time, which tells you escalation is a structural feature of contract billing, not an edge case.
Tiered and slab pricing: each unit priced at its own tier's rate
Volume-based contracts often price the first block of usage at one rate and higher blocks at another. The naive, wrong way to implement this is "find which tier the total quantity falls into, charge that rate for everything." Our tiered calculator does it properly: it walks the tiers in sequence, and for each tier it bills only the units that actually fall inside that tier's from_quantity/to_quantity range at that tier's rate, before moving to the next. A usage total that crosses three tiers gets billed as three separate slices, each at its own rate, and the invoice line item carries a full tier breakdown so the person approving the invoice can see the math, not just the total.
Tiers are stored as rows with a sequence field, sorted before evaluation, and the top tier's upper bound can be left open (to_quantity null) for uncapped usage. We have a second, older tiered calculator in our SaaS billing path that works on a simpler tier shape and explicitly validates that there are no gaps between adjacent tiers before it will compute anything, refusing to bill against a slab configuration that doesn't tile cleanly across the full usage range.
Usage-based billing: metered, and prorated at the record level
For consumption contracts, the engine pulls usage records for a billing period, groups them by item, and sums the quantity before pricing it (flat-rate or tiered, per item). The detail that doesn't show up in a product demo: usage records don't always align to the invoice period's start and end dates. A usage record that starts three days before the billing period closes and continues into the next period gets apportioned by day overlap, not counted in full or dropped. The engine computes the ratio of overlapping days to the record's total span and prorates the quantity accordingly, before that quantity ever reaches the pricing calculation. That's the kind of boundary condition a person doing this in Excel either misses or has to remember to check every single month.
Free quota and minimum commitments: floors, ceilings, and rollover credits
Two separate mechanisms cover this, because "free quota" and "minimum commitment" are different shapes of the same idea.
Free or included quota works as a subtraction before pricing: the engine takes actual quantity, subtracts whatever quantity is marked included on the contract, floors the result at zero, and only prices what's left. A fully free line item is recognized and zeroed out rather than priced at zero, which keeps the invoice line item semantically correct instead of just numerically correct.
Minimum commitment is the mirror case, and it's the more involved one. If actual usage in a period comes in under the contractual minimum, the invoice line is rewritten to bill the minimum instead of actual usage, and the line item is labeled as minimum billing so it's clear on the invoice why the number doesn't match raw usage. If usage exceeds the minimum, the engine computes the excess separately. And minimums aren't billed as a flat floor regardless of period length: when a contract has a calendar-month clause, the minimum itself is prorated by the ratio of days actually billed in that period to the days in the full calendar month, so a contract that starts on the 20th of the month doesn't get charged a full month's minimum for eleven days of service.
There's also a rollover mechanism: usage below the minimum in one period can generate credits that reduce billing in a later period, and those credits can expire per the contract's configured schedule. That's a genuinely more complex object than a simple minimum floor, and it's live in the codebase, not a planned feature.
Proration for mid-cycle changes: period-based, not amendment-based
Worth being precise about what our proration actually covers today. When a billing period doesn't run from the first to the last day of a calendar month, whether because a contract starts mid-month or a billing period is otherwise short, the engine computes the ratio of actual period days to the days in that calendar month and scales the charge accordingly. That daily-ratio math is leap-year safe by construction, because it derives the days-in-month figure at run time rather than assuming 30 or 31.
What we don't yet have is a first-class contract amendment object, something that would let you say "this contract's terms changed on this date, prorate the old terms against the new terms within the same period." Today, a contract change is handled by ending one contract period and starting a new one, and proration works cleanly across that boundary because it's built on billing-period math, not on knowledge of what changed. It's an honest limitation, and one more reason we're flagging it here rather than describing it as solved.
We also have a proration strategy setting on some contracts with values for daily, full-period, none, half-month, and custom modes, but only the daily and full-period paths are actually wired into the calculation today. The other modes are declared in the schema and not yet load-bearing. [NEEDS REAL EXAMPLE] if we want to say more about the roadmap here, we should confirm with engineering before publishing rather than imply it's shipped.
Why this has to be deterministic, not a model guessing at a contract
None of the mechanisms above are AI making a judgment call. Escalation compounding, tier walking, usage proration, minimum-commitment floors, they're all explicit, auditable arithmetic against fields stored on the contract, and every computed invoice can be traced back to the exact rate, tier, or ratio that produced each line. That matters because the invoice has to survive two different kinds of scrutiny: the customer's accounts payable desk checking HSN codes and place-of-supply and TDS sections, and the finance person inside our customer's company who has to sign their name to the number before it goes out.
Where AI does show up in our system is upstream of that computation, and in a human-in-the-loop step, not instead of one. Every invoice our engine generates, whether cloned, escalated, tiered, or usage-based, is created in draft status. Nothing gets an invoice number, and nothing goes out, until a person explicitly approves it. That approval step is where the line items, the tier breakdown, and the computed totals are visible to the person who's accountable for them. Deterministic computation gets the number right the same way every time; the approval step is where a human still gets to look at the math before it becomes a legal document.
The pattern from the LinkedIn post was that contract-to-invoice logic has no owner. Our answer isn't to make software own it silently. It's to compute it the same way a careful person would, show the work, and put a person back at the point where they sign off.
If your finance team is still the only place your contract logic lives, we'd like to see the contract.