{"openapi":"3.1.0","info":{"title":"Penny Rounding","version":"1.0.0","summary":"What a cash total actually rounds to now that the penny is gone.","description":"Round a cash transaction the way the post-penny rules actually work: on the final total, after tax, for cash payments only. Shows the exact total alongside the rounded one, names the rule being applied, and computes the real expected rounding effect across a price list — which is close to zero, not the free margin it is widely assumed to be.\n\n**Not for:** Not legal, tax, or accounting advice. Federal rounding legislation was not enacted as of September 2026, state law varies and is changing quickly, and a rounding practice that is lawful in one state may not be in another. This computes arithmetic under a rule you select; it does not tell you which rule applies to your business. Check your own state law and your own tax authority.\n\nAmounts are integer cents internally. In requests, a STRING is read as dollars (\"19.99\") and a NUMBER as integer cents (1999); a non-integer number is refused.\n\nFree tier: 250 calls per UTC day per caller. Past that, endpoints return 402 with x402 payment requirements. An MCP server exposing the same capabilities is available at https://penny-rounding.gumballtools.com/api/mcp.","contact":{"url":"https://github.com/bwalvoord/gumball"}},"servers":[{"url":"https://penny-rounding.gumballtools.com","description":"Production"}],"externalDocs":{"url":"https://penny-rounding.gumballtools.com/llms-full.txt","description":"Full documentation for agents"},"paths":{"/api/v1/settle":{"get":{"operationId":"settleTransaction","summary":"Round a cash total: exact subtotal, tax on it, then round the total.","description":"Returns the exact subtotal, tax, exact total (what a card is charged), the rounded amount due, the signed delta, which final digit decided it, warnings, and a disclaimer. Card payments are never rounded, whatever rule is passed.","parameters":[{"name":"subtotal","in":"query","required":true,"description":"Pre-tax subtotal as a dollar string (\"19.99\") or integer cents (1999). For line items, POST an `items` array instead — rounding still applies only to the final total.","schema":{"type":"string"}},{"name":"taxRatePercent","in":"query","required":false,"description":"Sales tax as a percentage (8.25, not 0.0825). Supply it whenever known: rounding is applied after tax, so the rate decides which nickel the total lands on.","schema":{"type":"number","minimum":0,"maximum":100,"default":0}},{"name":"tender","in":"query","required":false,"description":"cash (default) or card. Card, EFT, and gift-card payments are priced to the cent and are never rounded.","schema":{"type":"string","enum":["cash","card"]}},{"name":"rule","in":"query","required":false,"description":"symmetric (default) is the Common Cents Act and Canadian method: 1, 2, 6, 7 round down; 3, 4, 8, 9 round up. always-down is Indiana's rule for cash payments of tax, not a retail rule. always-up is not law anywhere for retail. none is what a card gets.","schema":{"type":"string","enum":["symmetric","always-down","always-up","none"]}}],"responses":{"200":{"description":"The settled transaction.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input. The body names the field and how to fix it.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","fix_hint","docs"],"properties":{"code":{"type":"string","description":"Stable machine-readable error code."},"message":{"type":"string"},"fix_hint":{"type":"string","description":"What to change before retrying."},"field":{"type":"string"},"token":{"type":"string"},"examples":{"type":"array","items":{"type":"string"}},"docs":{"type":"string","format":"uri"}}}}}}}},"402":{"description":"Free quota of 250 calls per UTC day exhausted. Body is an x402 v1 payment requirement ($0.001 per call). While settlement is disabled the body carries payment_enabled: false and the call cannot be unblocked by paying — wait for quota.resetsAt.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/impact":{"get":{"operationId":"roundingImpact","summary":"What rounding is actually worth across a price list.","description":"Returns the mean delta per cash transaction, the up/down/unchanged split, a per-thousand figure, a per-price breakdown, and a plain-language verdict. Symmetric rounding nets to approximately zero; this computes the real number for a real price list rather than asserting it.","parameters":[{"name":"prices","in":"query","required":true,"description":"Comma-separated prices, e.g. 0.99,4.99,19.99. POST an array for longer lists. The answer depends on the final digits of your own prices, so there is no useful generic answer.","schema":{"type":"string"}},{"name":"taxRatePercent","in":"query","required":false,"description":"Sales tax as a percentage (8.25, not 0.0825). Supply it whenever known: rounding is applied after tax, so the rate decides which nickel the total lands on.","schema":{"type":"number","minimum":0,"maximum":100,"default":0}},{"name":"rule","in":"query","required":false,"description":"symmetric (default) is the Common Cents Act and Canadian method: 1, 2, 6, 7 round down; 3, 4, 8, 9 round up. always-down is Indiana's rule for cash payments of tax, not a retail rule. always-up is not law anywhere for retail. none is what a card gets.","schema":{"type":"string","enum":["symmetric","always-down","always-up","none"]}}],"responses":{"200":{"description":"The aggregate effect.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input. The body names the field and how to fix it.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","fix_hint","docs"],"properties":{"code":{"type":"string","description":"Stable machine-readable error code."},"message":{"type":"string"},"fix_hint":{"type":"string","description":"What to change before retrying."},"field":{"type":"string"},"token":{"type":"string"},"examples":{"type":"array","items":{"type":"string"}},"docs":{"type":"string","format":"uri"}}}}}}}},"402":{"description":"Free quota of 250 calls per UTC day exhausted. Body is an x402 v1 payment requirement ($0.001 per call). While settlement is disabled the body carries payment_enabled: false and the call cannot be unblocked by paying — wait for quota.resetsAt.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/price-band":{"get":{"operationId":"priceBand","summary":"Which nearby list prices settle to the same cash total.","description":"Rounding collapses a band of five consecutive list prices onto one nickel. Within that band the cash collected is identical, so the highest list price in it is strictly better — same from cash customers, up to 4 cents more from card customers. Returns the band, the highest price in it, and the free gain.","parameters":[{"name":"listPrice","in":"query","required":true,"description":"Dollar string (\"19.99\") or integer cents.","schema":{"type":"string"}},{"name":"taxRatePercent","in":"query","required":false,"description":"Sales tax as a percentage (8.25, not 0.0825). Supply it whenever known: rounding is applied after tax, so the rate decides which nickel the total lands on.","schema":{"type":"number","minimum":0,"maximum":100,"default":0}},{"name":"rule","in":"query","required":false,"description":"symmetric (default) is the Common Cents Act and Canadian method: 1, 2, 6, 7 round down; 3, 4, 8, 9 round up. always-down is Indiana's rule for cash payments of tax, not a retail rule. always-up is not law anywhere for retail. none is what a card gets.","schema":{"type":"string","enum":["symmetric","always-down","always-up","none"]}},{"name":"spreadCents","in":"query","required":false,"description":"How far either side to look. Beyond about 5 you are changing the price, not catching a rounding boundary.","schema":{"type":"integer","minimum":1,"maximum":50,"default":5}}],"responses":{"200":{"description":"The price band.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input. The body names the field and how to fix it.","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","fix_hint","docs"],"properties":{"code":{"type":"string","description":"Stable machine-readable error code."},"message":{"type":"string"},"fix_hint":{"type":"string","description":"What to change before retrying."},"field":{"type":"string"},"token":{"type":"string"},"examples":{"type":"array","items":{"type":"string"}},"docs":{"type":"string","format":"uri"}}}}}}}},"402":{"description":"Free quota of 250 calls per UTC day exhausted. Body is an x402 v1 payment requirement ($0.001 per call). While settlement is disabled the body carries payment_enabled: false and the call cannot be unblocked by paying — wait for quota.resetsAt.","content":{"application/json":{"schema":{"type":"object"}}}}}}}}}