Amortance.

Dealer software · buy-here-pay-here, trailers, trucks

The contract is printed once. The dates in it keep moving.

A payment is deferred to the end of the term. A service contract is canceled and the unearned premium comes back three weeks later. The vehicle is written off, and the insurer settles on its own schedule. Every one of those rewrites the contract, and the amounts aren’t small.

For platforms whose dealers carry their own paper: Amortance replays the contract from origination against the events that actually happened, including the periods already charged, and returns every figure with the rule and the effective date behind it. The dealer stays the creditor; the platform stays the system of record. It returns the arithmetic, and the sentence that explains it.

What is for sale

An annual license for the calculation layer. It starts with a review.

Who it’s for
Platforms whose dealers finance their own sales and carry the paper themselves — not lenders, and not the dealers directly.
What you license
The engine that answers what is owed and why, embedded in your product and configured to your contract rules. One integration covers every dealer on the platform. What the annual term covers.
How it starts
A review of 20–50 live contracts, which is also where your event catalog gets written down. What the review is.
What it costs
Two figures, in this order: a fixed integration fee, quoted from the catalog before any code is written; then the annual license, per platform and priced by how many active plans you carry. Where each one comes from.
What it computes
Simple interest on the outstanding principal, on an actual/365, actual/360 or 30/360 basis, accrued interest settled before principal. Precomputed and add-on contracts — Rule of 78s and its relatives — aren’t covered, and if that’s what your dealers write, this isn’t for you.
What exists today
The calculation core is public and runnable — the worked example below is computed by it. In build: multi-tenancy, your event catalog, the API.

The events

What a book actually sees.

Each one has a date it happened, a date the money moved, and a contract that says which of the two governs.

  • A deferred paymentMoved to the end of the term, often with a fee financed into the balance. The interest that accrued in the gap doesn’t disappear — it’s paid out of the next installment, and the customer sees a balance that barely moved.
  • A canceled service contract or GAP policyThe service contract refunds its unearned premium pro rata; the GAP waiver quoted further down refunds its fee in full inside sixty days of the date it took effect, nothing after that, and nothing at all once the vehicle is a total loss — at which point the same document treats the fee as fully earned. Either way the money arrives weeks after the cancellation and is credited to the balance. Credited as of which date?
  • A total lossThe insurer pays actual cash value on its own timetable. The payoff on the date of loss and the payoff on the date of settlement are different numbers, and the waiver names one of them.
  • A trade-in with negative equityThe remaining balance of one contract is rolled into the next. Both have to be right, and the first one has to be closed on a stated date.
  • Repossession with a deficiencySale proceeds, allowable expenses and a balance that has to be defensible line by line, long after the fact.

Three of the five are followed to the cent in the worked example below and published as test vectors: a deferral, a canceled service contract, a total loss. Cancelling the GAP policy itself, trade-ins and repossession deficiencies are in the catalog and not yet in the published vectors.

Worked example · synthetic data, real mechanics

One contract, three events, and three dates that decide the money.

Every event here has two dates: the day it takes effect and the day it reached the system. The second event does what the others can’t — using that gap, it changes a payment already collected, reconciled and reported.

Amount financed
$15,398.69 · 19.90% · actual/365
Payment
$235.35 every 14 days, 91 payments, Fridays

Event one: a payment is deferred

On July 3 payment 12 is moved to the end of the term and a $25 fee is financed. The customer expects to lose two weeks. What actually happens is that the interest from the skipped period is paid out of the next installment:

PostingTo interest, $To principal, $Balance, $
Payment 11 · June 19108.23127.1214,052.15
Deferral fee financed · July 314,077.15
Payment 13 · July 17214.7120.6414,056.51
Payment 14 · July 31107.29128.0613,928.45

Payment 13 pays $107.26 of interest left unpaid from the skipped period plus $107.45 from its own, so $20.64 of it reaches principal instead of the usual $129.07. That is the answer to “why hasn’t my balance gone down”. It is also a policy: this contract lets the interest accrue through the gap. Another contract — or another system — suspends it instead, and that’s a different number, as the manuals below show.

Event two: the service contract is canceled

Canceled on August 7 with 203 of 730 days used. The refund takes two steps: $1,795.00 × (1 − 203 ÷ 730) = $1,295.84; $1,295.84 − $25.00 = $1,270.84. The administrator’s check arrives on August 28. Credited as of which date?

The refund is creditedLast payment no.Final payment, $Interest over the life, $
On the cancellation date, August 783127.025,272.87
When the money arrives, August 2883151.465,297.31
Twenty-one days of float24.4424.44

Either reading is defensible and the contract decides. What isn’t optional is knowing the price: the raw interest on $1,270.84 for twenty-one days is $14.55, but carried to the end of the term it costs the customer $24.44. The refund itself pulls the contract in by 10 payments, finishing on 2029-04-06 instead of 2029-08-24.

What that does to a payment already taken

Payment 15 was collected on August 14 — after the cancellation, before the check. It wasn’t reversed. It was re-split against the balance the refund had already reduced, effective August 7 — three weeks before the check itself arrived.

Payment 15 · August 14To interest, $To principal, $Balance after, $
As split on the day it was collected106.31129.0413,799.41
Replayed, refund effective August 7101.47133.8812,523.73
Difference−4.844.84−1,275.68

This is the difference the product is about. Same payment, same day, same contract: $235.35 either way. What moves is $4.84 of it, out of interest and into principal, because an event that took effect on August 7 didn’t reach the system until the 28th. The last column is the refund of $1,270.84 plus that $4.84. A ledger that only moves forward can credit the refund from the day the money arrives; what it can’t do is go back and re-split a payment it has already posted, reconciled and reported — so the split stays where the late event left it, and every payment after this one inherits the difference.

Event three: a total loss

The vehicle is destroyed on October 2. The insurer pays actual cash value of $9,850.00 on October 24. The principal is $12,101.25 on both dates; only the accrued interest isn’t. Two assumptions this example makes, stated rather than left in the file: the service contract refund is credited on the cancellation date, and no payment is collected after the loss — the waiver quoted below tells the borrower to keep paying and to have those payments refunded instead, which is a third policy and a different final figure.

Payoff measuredAccrued, $Payoff, $
At the date of loss, October 246.1812,147.43
At the date of settlement, October 24191.3312,292.58
Twenty-two days145.15145.15

Twenty-two days at just under $6.60 a day. Against actual cash value of $9,850.00 the deficiency is $2,297.43 measured at the loss and $2,442.58 measured at settlement — and the waiver names one of them.

The waiver in this contract names the deficiency as at the date of loss and then excludes two things it spells out: amounts added to the balance after origination — the $25.00 deferral fee — and any installment that fell due and was never collected, which is payment 12, deferred in July to a date three years past the loss at $235.35. Of the $2,297.43 deficiency it therefore forgives $2,037.08, and the customer, who has every reason to believe insurance settled the matter, is left with this:

Owed on October 24, when the insurer pays12,292.58
Less actual cash value, paid by the insurer9,850.00
Less the deficiency waived as at October 22,037.08
Left standing on the account405.50

$405.50: the $145.15 that accrued while the claim was in the mail, plus the $260.35 the waiver excludes — the deferral fee and the payment skipped in July, neither of them the cancellation fee, which came off the refund weeks earlier. Whether the dealer collects it, waives it or writes it off is a business decision — but it has to be a decision, recorded as its own event, rather than a number nobody can explain on a statement.

Check it yourself

The engine is published as engine.py, this example’s complete input as a file, and a second implementation — written from the specification, sharing no code with the engine — recomputes every figure in it. Both are MIT.

curl -O https://amortance.com/bhph.json
curl -O https://amortance.com/verify.py
python3 verify.py bhph.json

Add --all to print every figure it derived, next to the published one.

Your own book

Four questions to put to your own system.

Nothing above proves anything about you — it’s a synthetic contract with real mechanics. These four you can answer this week, without me and without an export.

Do you get these events at all?
Take one quarter of contracts and count the ones touched after signing by a deferral, a canceled service contract or GAP policy, a trade-in with negative equity, a repossession or a total loss. If that count is a handful, stop reading.
Which date do you credit?
Take one canceled service contract. Find the date your system credited the refund, then find the date the contract says it takes effect. If those are the same date because somebody chose it, good. If they’re the same date because nobody chose, that’s a different answer.
What happened to the interest already charged?
On that same contract, find a payment collected between the date the contract says the refund takes effect and the day the money actually reached you — two dates that differ even when the pair above didn’t. Was that payment’s split between interest and principal ever revisited? If it wasn’t, the difference is still in the balance, and every payment after it inherits it.
Who answers when the customer asks?
Find the last ticket that asked why a balance was what it was. Count the hands it passed through and the days it took. That is the part of the cost you already pay.

If all four come back clean, you don’t need this layer, and knowing that costs you an afternoon. If the second and third don’t, the review is where a number gets put on it — on your contracts rather than on mine.

Or answer it with figures instead of questions. self-check.txt is one short contract — twelve monthly payments, one credit that takes effect three weeks before the money arrives — with both answers side by side. Key it into your own module, compare eight figures, and whichever column you land in is the policy your system applies today. No export, nothing sent to me, and the inputs and both columns are published as self-check.json for your own verifier to re-derive.

What the category documents

The dates are in the contracts. The deferral is in the manual.

Frazer — a DMS for independent used-car dealers with buy-here-pay-here built in, used by its own count by more than 19,000 dealers in all 50 states — documents exactly what its deferral does. Pausing payments “acts as if you pressed the refinance button twice”: the first refinance “will set the customer’s new due date as well as change the customer’s APR to 0%”, and on the resume date “the APR will automatically change back to the original APR”; the new document “will outline the length of time that the customer’s account will not be accruing interest”. The schedule is carried forward rather than rebuilt — “the customer's original amortization schedule stays in tact”, “you WILL STILL COLLECT THE SAME NUMBER OF PAYMENTS OVER THE LIFE OF THE LOAN”, and the balance shown in the meantime is “more of an amortization schedule quirk than a full re-calculation of the payment schedule”. That is one policy, written down and defensible: at Frazer a deferral costs the customer no interest, and the modification the customer signs states the period. It is not the policy in the example above, where interest keeps accruing through the gap and the next installment pays $214.71 of its $235.35 into it. Both are legitimate, and which applies is a question for your own paper rather than for either vendor: the difference is money on every deferred deal, and a system that can’t say which reading it applied can’t be checked against either. (Frazer help manual, Pause Payments.)

The lag is written into law too. Washington lets a holder who has made no claim return the contract for the full purchase price inside the first ten days; from day ten to day thirty the provider may charge “a cancellation fee not exceeding $25” — the figure the example above uses; and after thirty days the refund is pro rata “based upon either elapsed time or mileage”, “less a cancellation fee not exceeding $25”, with a 10 percent penalty on any refund “not paid within 30 days of return of the contract”. Every one of those is a condition rather than a default: whether the refund is owed at all, from which day it is measured, on which basis it is computed, and by when it has to be paid. And the statute backdates the event rather than posting it — on return the contract is “void from the beginning and the parties are in the same position as if no contract had been issued”. That sentence is about the service contract rather than about the financing: which date the refund is credited as of in the loan ledger is a term of the retail installment contract, not of this statute. What the statute does show is where those dates come from — and a ledger that can only credit money on the day it arrives has one answer available to it whatever the paper says. (RCW 48.110.075.)

The dates in the total-loss example aren’t mine either. One credit union’s published GAP waiver addendum — the only one I could read in full, so the only one worth quoting — defines the Unpaid Net Balance as what is owed “to clear the outstanding Installment Loan account upon the Date of Loss”, and excludes “Skipped Payments” — which it defines as “any missed payment approved by the lender as part of a Lender Skip-a-Pay program” — along with “amounts that are built into or added to the Installment Loan balance after the inception date of the Installment Loan”. Those are the two exclusions the table above applies: the payment deferred in July and the fee financed with it. The same document also excludes amounts arising from the term extension a skipped payment causes, which is wider than one installment; the example takes the narrow reading, and says so rather than quietly taking the larger number. (Sample GAP Waiver Addendum.)

What stays with you

Everything that touches money or people.

  • The layer moves no money, stores no cards and sends nothing to your processor.
  • I don’t need borrower names or contact details — amounts, dates and terms carry the calculation, and the export is deleted once the review is delivered.
  • It isn’t the creditor, the servicer or the collector, and it doesn’t become one.
  • I don’t decide who gets financed. No scoring, no underwriting, no risk model.

Your DMS may already replay past periods. None of the products I could read says it does — Frazer documents its deferral, not a replay — but absence of documentation isn’t absence of the feature. The published vectors tell you whether your engine agrees with ours on these two contracts; a review tells you whether it agrees with your own contracts on your own book. If it does, that’s a result worth having in writing.

Interface draft · not shipped

What you’d actually be integrating.

The API is in build, so the shapes below are a draft and not a contract — the first partners’ catalogs settle them. What isn’t a draft is the division of labor: you send terms and events, you get back figures with the rule and the arithmetic behind each, and every decision about your books stays on your side.

What your product sends

The contract, its history, and the event that just arrived with both of its dates. on is the date the event takes effect and received the date it reached you: the whole product lives in the gap between them. The history has to be there because a replay derives the old answer as well as the new one; the postings and earlier events are abridged below and go in full in a real call. The worked example’s complete input is published as bhph.json.

POST /v1/replay
{
  "contract": {
    "sold": "2026-01-16",
    "amount_financed": "15398.69",
    "apr": "0.1990",
    "day_count": "actual/365",
    "payments": {
      "first": "2026-01-30",
      "every_days": 14,
      "count": 91
    }
  },
  "history": {
    "events": [
      {
        "kind": "payment_deferred",
        "on": "2026-07-03",
        "received": "2026-07-03",
        "installment": 12,
        "moved_to": "end_of_term",
        "fee_financed": "25.00"
      }
    ],
    "postings": ["...payments 1-15..."]
  },
  "event": {
    "kind": "service_contract_canceled",
    "on": "2026-08-07",
    "received": "2026-08-28",
    "premium": "1795.00",
    "term_days": 730,
    "cancellation_fee": "25.00"
  },
  "restate_from": "2026-08-07"
}

What comes back

The answer to the re-split table in the worked example. Not a balance: the postings whose split the event changes, each with what it was, what it becomes, and the rule that moved it.

{
  "refund": {
    "gross": "1,295.84",
    "net": "1,270.84"
  },
  "restated": [{
    "posting": "payment 15",
    "on": "2026-08-14",
    "amount": "235.35",
    "was": {
      "interest": "106.31",
      "principal": "129.04"
    },
    "now": {
      "interest": "101.47",
      "principal": "133.88"
    },
    "because": {
      "event": "service refund",
      "effective": "2026-08-07",
      "amount": "1,270.84",
      "rule": "simple interest"
    }
  }],
  "term": {
    "was": "2029-08-24",
    "now": "2029-04-06",
    "payments_fewer": 10
  }
}

What you do with what comes back is your call — read the derivation to whoever asked, restate the period, or turn the difference into an entry with an effective date and a reason on it. The service never writes to your books and holds no credentials that would let it. It runs either as a service you call, which needs no borrower identity, or as the same engine as a library inside your own deployment, which sends nothing anywhere: identical arithmetic, same test vectors, and which shape gets built first is the first partner’s call rather than an announcement here.

Build or buy

The core is MIT, which makes building it yourself easier, not harder.

So the question isn’t whether your team could write this. It’s what they’d be taking on, and whether it belongs on your roadmap for as long as the platform runs.

What is already written down
Six kinds of post-signature event across two contract types, each replayed under four combinations of day count and policy, plus the short self-check under both readings of one date — 588 published figures that a second implementation re-derives from the specifications alone. That is the regression suite you’d otherwise be writing first.
The combinations, not the events
One event on its own is the case everybody gets right. A deferral, then a refund credited at a date in the past, then a total loss whose waiver excludes what the deferral added — that is where two correct-looking implementations disagree, and it’s what the examples are built around.
Rules change; old answers must not
When a rule changes, the vectors for the old rule stay and the new ones are added beside them, so a figure you gave a customer two years ago still reproduces. Keeping that true is the maintenance: a rule change isn’t a patch but a new set of vectors beside the old, and it doesn’t end while the platform runs.
What you keep either way
The core, its vectors and the event catalog written during the review are yours under MIT whichever way you decide. Nothing here is an argument that you couldn’t leave later, and I’d rather you bought this knowing that.

The case for building is that you’d own it outright, and it’s a real one. Two things sit on the other side. The first is the maintenance above: a standing line on your roadmap, or a line on an invoice. The second is what you’d be building from. Your own engine gets written against your team’s reading of your own contracts, and that reading is the thing the review exists to check; build first and you have shipped it into the calculation before anyone has held it up against the paper. For me this is the whole job, and for you it’s one corner of one.

How it starts

A review of 20–50 live contracts.

You send an export, I replay every contract against the terms your own contracts actually state, and you get back a per-contract comparison — including the periods already accrued.

Where it leads is the calculation layer embedded in your platform: the review is where the rules of your book get written down. An integration configured from assumptions instead would return confident wrong numbers.

Email me about a review No integration, no credentials, nothing to install
Scope
One contract type and an agreed set of events. An export from you; anonymized is fine.
What you send
Terms, the payments collected, and every change after the sale with the date it takes effect. What that has to contain, what it may leave out, and what to do when you don’t store the second date: sample-export-dealer.txt, with sample-export-dealer.csv as rows.
Delivery
10 business days from the agreed start, once the export and the contract rules are complete.
Result
A comparison for every contract, the reason for each difference, and the actions your team can take. A sample of the shape it arrives in: sample-review-dealer.txt and sample-review-dealer.csv, on synthetic data.
Your data
The export is processed on my machine, never uploaded anywhere, and deleted once the review is delivered — or sooner, on request.
Reviewer
Evgenii Agafonov, founder. In consumer lending since 2022 as a developer and tech lead; I do the reviews myself.

And then what

From the report to a running integration.

The review can end the conversation, and often should. If it doesn’t, this is the path — and none of it starts before the report is in your hands.

1 · The report
A comparison for every contract in the export, and the rules your book actually follows written down. If your own module agrees on all of them, that’s the result — and the question left is what producing those answers and keeping them right already costs you: the reconstructions by hand, the support time, the developer days every time a contract term moves. If that’s cheap where you sit, there’s nothing further to buy, and I will say so.
2 · The scope
If it doesn’t agree — or it agrees and keeping it that way is the cost — the catalog from the review says which events the layer has to cover, which of your systems calls it and what comes back. That becomes a written scope and a fixed integration fee, quoted from the catalog before any code is written.
3 · Acceptance
The layer ships with its test vectors and with the plans from your own review as cases. Your team runs them through the integration, called from your own product, in the environment you choose. Agreement on every one is the acceptance test — a number you check, not one I assert.
4 · The annual term
Begins at acceptance rather than at signature, and is priced per platform on a band of active plans. What it buys is keeping the calculation, your rules and the test vectors in step with your contracts and your product, for as long as you run it.

Two figures are missing from this page: the integration fee and the annual band. The first follows from the catalog, the second from how many active plans you carry, and I’d rather quote both against your own book than publish a figure I would have to take back. Both are named in writing and fixed before any integration work starts, which is the part that matters more than seeing them here.

Questions

Asked first, every time.

What happens to the integration if Amortance goes away?

The standard procurement question, and a fair one for anything that sits in your core. Two things are already true and cost you nothing: the calculation core and its test vectors are published under MIT, so the arithmetic and the cases that prove it stay yours to run and to check a replacement against; and the event catalog written during the review is your document, not mine. Neither of those keeps a hosted layer running — a license can’t promise that, and I’m not going to read it as if it could. What goes beyond that — how the embedded layer is licensed, which continuity guarantees belong in the contract — is worth settling in writing with your procurement rather than promising on a web page.

Does this change our licensing position?

Nothing here makes anyone a creditor who wasn’t one already: the dealer is the creditor before and after, I take no fees from borrowers, hold no funds and originate nothing. Whether that changes anything in your own licensing is a question for your counsel and your states rather than for a vendor’s FAQ — what I can hand them is a precise description of what the layer touches and what it doesn’t.

Our dealers run mixed stock — cars and trailers.

That is exactly why policy belongs to the contract rather than the dealer. Rate caps, disclosures and permitted charges differ by what was sold and where; the engine takes them as terms, one contract at a time.

We already have an amortization module.

Most platforms in this category do. The question the review answers is narrower: on your own book, does it still agree with the contract after a deferral, a refund and a settlement? If it does, you get that in writing.

What if the review finds nothing?

You get a clean result in writing, with the rules your book is actually following stated explicitly. A review that finds nothing is a good outcome, and for most platforms that is where it ends. The one case where it doesn’t is a book whose answers are right and expensive to keep right — the reconstructions by hand, the support time, the developer days on every contract term that moves. That cost is yours to weigh, and the catalog from the review is what an integration would be quoted from.

Files

Ten files, each with one job.

In the order they usually get opened. Nothing needs an account, sits behind a form, or asks you for anything first.

Try it against your own system

self-check.txt
One short contract — twelve monthly payments and a credit that takes effect three weeks before the money arrives — with both answers side by side. Key it in, compare eight figures, and whichever column you land in is the policy your system applies today. Twenty minutes, and nothing leaves your building.
self-check.csv
The same eight figures as rows, for a script rather than for the eye.
self-check.json
The inputs and both sets of results in machine form, so your own engineer can re-derive them instead of trusting the table.

See what the review produces

sample-review-dealer.txt
A finished review on synthetic data: which figures were checked, which agreed, which didn’t, the contract rule each was checked against, and the action for each difference. The form is real; the contract is invented.
sample-review-dealer.csv
The same comparisons as rows, one per check.

See what you would have to send

sample-export-dealer.txt
What an export has to contain: the terms, the payments actually collected, every change after the sale with the date it takes effect, and the figures your own system produced. And what it must never contain — names, contact details, card numbers: none of that is needed to check the arithmetic.
sample-export-dealer.csv
One contract exported that way, as an example of the shape. Your own columns are fine.

Check the arithmetic itself

engine.py
The calculation core that produced every figure on this page. MIT, runnable, no dependencies, yours to keep whatever you decide.
verify.py
A second implementation that shares no code with the engine and re-derives all 588 published figures from the specifications alone. It is the reason none of the numbers here has to be taken on trust.
bhph.json
This page’s example in full: the terms, every posting, and the expected results under four combinations of day count and policy. This is what verify.py reads.

Start here

Two sentences are enough.

What your dealers put on a contract, and what changes after the sale most often. Within a day you get back: whether the layer applies to the change you describe, what checking it on your own book would take, and a plain no when it doesn’t apply.

hello@amortance.com No form and no call required. Written by default, so every answer arrives with its figures attached. Or check first whether the data even exists on your side: what an export has to contain.