{"id":"loan-amortization-schedule","version":"1.0.0","description":"Cent-exact level-payment loan schedule with per-period rounding, extra payments, dates and a row window.","supported_operations":["loan amortization schedule","mortgage payment schedule with extra payments","remaining loan balance after n payments","interest saved by paying extra on a loan","payoff date of a loan with extra payments","monthly principal and interest breakdown","amortization table with payment dates","biweekly loan payment schedule"],"unsupported_operations":["solving one unknown of the annuity equation such as the rate or number of periods (use tvm-solve)","regulatory APR including prepaid fees (use loan-apr-compute)","variable-rate, interest-only, negative-amortization or balloon-refinance loans with rate changes","day-count based interest such as actual/360 daily accrual"],"semantics":["Inputs: principal (decimal, greater than 0 and below 1e15), annual_rate (decimal 0 to 10 inclusive, a fraction), periods (integer 1 to 600, the scheduled number of payments), payments_per_year P (one of 1, 2, 4, 12, 24, 26, 52; default 12), scale (0 to 12, default 2, the number of fractional digits of every amount) and rounding (default half-up). principal, payment and every extra amount must have at most scale fractional digits (trailing zeros beyond scale are ignored: the value must be an exact multiple of 10^-scale), otherwise invalid_input, so every balance is an exact multiple of 10^-scale.","Periodic rate i = annual_rate / P (exact). Each row k = 1, 2, ...: opening balance B (B_0 = principal); interest_k = round(B * i) to scale with rounding (one rounding of the exact rational product, so per-period cent rounding is part of the contract); due_k = B + interest_k.","Level payment: when payment is omitted it is round(principal * i / (1 - (1+i)^(-periods))) to scale with rounding (principal / periods when annual_rate is 0); a computed payment that rounds to 0 is not_computable (zero_payment). When payment is given it must be greater than 0 and at least interest_1, otherwise not_computable (negative_amortization); it is used as given and is not rounded.","Regular payment of row k: for k < periods it is min(payment, due_k); for k = periods it is due_k (the adjusted final payment that closes the schedule exactly, so the final payment can differ from the level payment by rounding drift or be a balloon when a smaller payment was supplied).","Extra payments: extra_payments is a list (at most 600 items) of {period, amount} with period an integer 1 to periods and amount greater than 0; items with the same period are summed. extra_per_period (decimal at least 0) adds a recurring extra in every period k >= extra_start_period (integer 1 to periods, default 1); extra_start_period without extra_per_period is invalid_input. The requested extra of row k is the sum of both. The applied extra is min(requested, due_k - regular payment of row k), so nothing is applied in the final scheduled period and nothing beyond the balance. principal_k = regular payment - interest_k + applied extra (principal includes the extra) and balance_k = B - principal_k. The schedule ends at the first row whose balance is 0: periods_actual is that row number (equal to periods when nothing pays it off early).","Row fields: period, date, payment (the regular payment), interest, principal (including extra), extra (the applied extra) and balance (after the row). Cash paid in a row is payment + extra. All amounts print with exactly scale fractional digits.","Dates: start_date (optional ISO date YYYY-MM-DD, year 1900 to 2200, a real calendar date) is the origination date and row k is dated k periods later: for P = 1, 2, 4 or 12 it is start_date plus k*12/P calendar months computed from start_date each time (never chained) with the day clamped to the month length (2024-01-31 plus 1 month is 2024-02-29, plus 2 months is 2024-03-31); for P = 26 it is start_date plus 14*k days; for P = 52 start_date plus 7*k days. start_date with payments_per_year 24 is unsupported_input. Without start_date every date is null and payoff_date is null; otherwise payoff_date is the date of the last row.","Row window: from_period (default 1) and to_period (default periods) are integers 1 to periods with from_period <= to_period (otherwise invalid_input). rows contains the rows k with from_period <= k <= to_period that exist (k <= periods_actual), ascending; include_rows false (default true) returns rows as an empty array. window_totals sums the same window (all zeros when it is empty) and totals sums every row of the schedule regardless of the window. Each total_* is the sum of the row values: total_paid = payment + extra, total_interest, total_principal (includes extras), total_extra.","baseline and savings are null unless extras were supplied (extra_payments non-empty or extra_per_period present, even when its value is \"0\"). Then baseline is {periods, total_interest} of the same loan with the same payment and no extras, and savings is {interest_saved = baseline.total_interest - totals.total_interest, periods_saved = baseline.periods - periods_actual}.","periodic_rate is i rounded to 10 fractional digits with rounding. periods_scheduled equals the input periods. The remaining balance after k payments is rows[k].balance when row k is in the window. The output is at most 400000 bytes; every allowed input stays under that.","Validation order: limits (periods above 600, more than 600 extra_payments items, a decimal string above 64 UTF-8 bytes) are limit_exceeded and checked before any parsing; then structure and ranges are invalid_input; then not_computable conditions.","Every rounded output is rounded exactly once from the unrounded value using the rounding mode: half-up (ties away from zero, the default), half-even, half-down (ties toward zero), up (away from zero), down (toward zero), ceiling, floor. Amount outputs print exactly scale fractional digits and rate-like outputs exactly rate_scale fractional digits; a result that rounds to zero prints without a minus sign (never '-0.00').","Decimal string fields (amounts, rates) must match ^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$ with at most 20 integer digits and at most 20 fractional digits; a JSON number, exponent form, leading plus, surrounding spaces, percent sign, missing integer part or trailing dot is invalid_input. Rates are decimal fractions (0.065 means 6.5 percent; \"6.5\" means 650 percent). A decimal string longer than 64 UTF-8 bytes is limit_exceeded, checked before parsing.","Disclaimer: arithmetic calculation only; not financial, tax, legal, or investment advice."],"limits":{"max_periods":600,"max_extra_payments":600,"max_output_bytes":400000,"max_string_bytes":64},"pricing":{"status":"unpriced","charge_usd":null},"input_schema":{"type":"object","additionalProperties":false,"required":["principal","annual_rate","periods"],"properties":{"principal":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"annual_rate":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"periods":{"type":"integer","minimum":1,"maximum":600},"payments_per_year":{"type":"integer","enum":[1,2,4,12,24,26,52]},"payment":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"start_date":{"type":"string","pattern":"^[0-9]{4}-[0-9]{2}-[0-9]{2}$","maxLength":10},"extra_payments":{"type":"array","maxItems":600,"items":{"type":"object","additionalProperties":false,"required":["period","amount"],"properties":{"period":{"type":"integer","minimum":1,"maximum":600},"amount":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64}}}},"extra_per_period":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"extra_start_period":{"type":"integer","minimum":1,"maximum":600},"from_period":{"type":"integer","minimum":1,"maximum":600},"to_period":{"type":"integer","minimum":1,"maximum":600},"include_rows":{"type":"boolean"},"scale":{"type":"integer","minimum":0,"maximum":12},"rounding":{"type":"string","enum":["half-up","half-even","half-down","up","down","ceiling","floor"]}}},"output_schema":{"type":"object","additionalProperties":false,"required":["payment","periodic_rate","periods_scheduled","periods_actual","payoff_date","rows","totals","window_totals","baseline","savings"],"properties":{"payment":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"periodic_rate":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"periods_scheduled":{"type":"integer","minimum":1,"maximum":600},"periods_actual":{"type":"integer","minimum":1,"maximum":600},"payoff_date":{"type":["string","null"],"maxLength":10},"rows":{"type":"array","maxItems":600,"items":{"type":"object","additionalProperties":false,"required":["period","date","payment","interest","principal","extra","balance"],"properties":{"period":{"type":"integer","minimum":1,"maximum":600},"date":{"type":["string","null"],"maxLength":10},"payment":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"interest":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"principal":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"extra":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"balance":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64}}}},"totals":{"type":"object","additionalProperties":false,"required":["total_paid","total_interest","total_principal","total_extra"],"properties":{"total_paid":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"total_interest":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"total_principal":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"total_extra":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64}}},"window_totals":{"type":"object","additionalProperties":false,"required":["total_paid","total_interest","total_principal","total_extra"],"properties":{"total_paid":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"total_interest":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"total_principal":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"total_extra":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64}}},"baseline":{"type":["object","null"],"additionalProperties":false,"required":["periods","total_interest"],"properties":{"periods":{"type":"integer","minimum":1,"maximum":600},"total_interest":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64}}},"savings":{"type":["object","null"],"additionalProperties":false,"required":["interest_saved","periods_saved"],"properties":{"interest_saved":{"type":"string","pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","maxLength":64},"periods_saved":{"type":"integer","minimum":0,"maximum":600}}}}},"examples":[{"input":{"principal":"8000","annual_rate":"0.1","periods":36,"from_period":1,"to_period":1},"output":{"payment":"258.14","periodic_rate":"0.0083333333","periods_scheduled":36,"periods_actual":36,"payoff_date":null,"rows":[{"period":1,"date":null,"payment":"258.14","interest":"66.67","principal":"191.47","extra":"0.00","balance":"7808.53"}],"totals":{"total_paid":"9292.94","total_interest":"1292.94","total_principal":"8000.00","total_extra":"0.00"},"window_totals":{"total_paid":"258.14","total_interest":"66.67","total_principal":"191.47","total_extra":"0.00"},"baseline":null,"savings":null}},{"input":{"principal":"10000","annual_rate":"0.06","periods":12,"extra_per_period":"100","start_date":"2025-01-15"},"output":{"payment":"860.66","periodic_rate":"0.0050000000","periods_scheduled":12,"periods_actual":11,"payoff_date":"2025-12-15","rows":[{"period":1,"date":"2025-02-15","payment":"860.66","interest":"50.00","principal":"910.66","extra":"100.00","balance":"9089.34"},{"period":2,"date":"2025-03-15","payment":"860.66","interest":"45.45","principal":"915.21","extra":"100.00","balance":"8174.13"},{"period":3,"date":"2025-04-15","payment":"860.66","interest":"40.87","principal":"919.79","extra":"100.00","balance":"7254.34"},{"period":4,"date":"2025-05-15","payment":"860.66","interest":"36.27","principal":"924.39","extra":"100.00","balance":"6329.95"},{"period":5,"date":"2025-06-15","payment":"860.66","interest":"31.65","principal":"929.01","extra":"100.00","balance":"5400.94"},{"period":6,"date":"2025-07-15","payment":"860.66","interest":"27.00","principal":"933.66","extra":"100.00","balance":"4467.28"},{"period":7,"date":"2025-08-15","payment":"860.66","interest":"22.34","principal":"938.32","extra":"100.00","balance":"3528.96"},{"period":8,"date":"2025-09-15","payment":"860.66","interest":"17.64","principal":"943.02","extra":"100.00","balance":"2585.94"},{"period":9,"date":"2025-10-15","payment":"860.66","interest":"12.93","principal":"947.73","extra":"100.00","balance":"1638.21"},{"period":10,"date":"2025-11-15","payment":"860.66","interest":"8.19","principal":"952.47","extra":"100.00","balance":"685.74"},{"period":11,"date":"2025-12-15","payment":"689.17","interest":"3.43","principal":"685.74","extra":"0.00","balance":"0.00"}],"totals":{"total_paid":"10295.77","total_interest":"295.77","total_principal":"10000.00","total_extra":"1000.00"},"window_totals":{"total_paid":"10295.77","total_interest":"295.77","total_principal":"10000.00","total_extra":"1000.00"},"baseline":{"periods":12,"total_interest":"327.96"},"savings":{"interest_saved":"32.19","periods_saved":1}}}],"execute_url":"/v1/tools/loan-amortization-schedule/versions/1.0.0/execute"}