Inspiration

The FTC alleged that one corporate landlord, Invitation Homes, returned 39.2% of renters' deposit dollars when the national average was 63.9%. The case settled for $48 million, and in March 2026 the FTC sent $47.2 million back to renters. New York's Attorney General recovered $422,000 for about 900 tenants of a single landlord who kept deposits without an itemised list. In England, 208,000 renting households say their deposit was never protected.

What struck me is that the law is usually already on the renter's side:

  • New York: a landlord who misses the 14-day deadline "shall forfeit any right to retain any portion of the deposit".
  • Texas, Georgia and Colorado: a renter can recover three times what was wrongfully kept.
  • England: a court "must" award one to three times the deposit if it wasn't protected in time.

Renters still lose, for three plain reasons:

  • They have no dated proof of what the home looked like on move-in day.
  • They don't know which event starts their state's clock. It isn't the same everywhere: in Texas it's a written forwarding address, and in Arizona it's a written demand counted in business days.
  • They don't know what the statute says happens when the landlord is late.

Keyback is built to close those three gaps.

What it does

1. Move-in: evidence a third party can check.

  • The renter walks each room with the in-app camera.
  • Every photo is fingerprinted with SHA-256 as it is saved.
  • The fingerprint alone is sent to an independent RFC 3161 timestamp server, which signs the moment the photo existed. The photo never leaves the phone.
  • Keyback builds a move-in report with every photo, its fingerprint and its timestamp, plus a page explaining how anyone can verify it with openssl. The renter sends it to the landlord that day.

2. Move-out: the landlord's actual deadline. The renter adds the dates their state's statute names. Keyback works out the last lawful day, and next to it shows:

  • the exact statute text it used;
  • how many days have passed;
  • what the statute says happens if the landlord misses it.

Examples: New York forfeiture, up to twice the deposit for wilful violations in New York, and three times in Texas.

3. The landlord's charges, lined up against the law. The renter enters what the landlord sent. Keyback flags what it can check mechanically:

  • a statement that arrived after the deadline;
  • a statement that doesn't itemise;
  • a charge the renter marks as normal wear and tear, shown beside the statute's wear-and-tear wording;
  • damage from a previous tenant;
  • a deposit above the state's cap.

It never decides who is right; it shows the words of the law.

4. The move-out pack (one RevenueCat purchase per tenancy):

  • a deposit request letter in the renter's own voice, with the statute quoted;
  • a charge-by-charge response to the landlord's deductions;
  • a small-claims evidence packet: timeline, findings, move-in beside move-out photos, fingerprints and timestamps;
  • in England, a letter before claim under section 214 of the Housing Act 2004 and a Tenant Fees Act 2019 repayment letter.

Keyback also reminds the renter the morning after the deadline passes.

What is always free:

  • the deadline and the statute quotes;
  • every check;
  • the timestamped move-in report;
  • the letters that start the clock in Texas, Ohio and Arizona.

A renter can protect themselves without paying. The pack saves them the work when a dispute actually happens.

Coverage: California, Texas, Florida, New York, Pennsylvania, Ohio, Georgia, North Carolina, Michigan, Washington, New Jersey, Colorado, Arizona, and England. Illinois and Massachusetts are listed as "not covered yet", because I couldn't verify their statute text, and Keyback doesn't guess.

How I built it

  • Flutter for Android, Dart 3.10. On-device JSON storage with atomic writes. No backend, no account, no analytics.
  • Rules from statute text, not from a model. assets/rules/ holds 67 rule rows. Each has the citation, the exact quote, the source URL and the date I checked it. The engine in lib/rules/ is pure, table-driven Dart. Every date and flag it produces carries the rule rows it relied on, so the screen can quote them. There is no AI anywhere in a decision.
  • Hand-built RFC 3161 timestamps.
    • The request is encoded in DER by hand (lib/evidence/).
    • The app tries freeTSA first, then DigiCert, then Sectigo.
    • Tests check the encoder byte for byte against real responses from all three servers, saved in test/fixtures/tsa/.
  • Tests: 152 unit and widget tests, with golden cases per statute that cite the quote they test. There is also an end-to-end integration test that drives the whole New York flow on an emulator.
  • RevenueCat: purchases_flutter and purchases_ui_flutter 10.12.0. The paywall is a RevenueCat Paywall designed in the dashboard. Details below.

How Keyback uses RevenueCat

The model follows the problem. People rent a place and leave it years later. A subscription would bill renters, who are already at risk of losing money, for months they don't need anything. So the move-out pack is a single purchase tied to one tenancy, sold at the moment it matters.

Implementation:

  • Product: a consumable, keyback_move_out_pack at $9.99, in the default offering, shown with RevenueCatUI.presentPaywall.
  • Unlocking: a purchase goes through Purchases.purchase(PurchaseParams.package(...)). Its transaction ID is written to the current tenancy, so one purchase unlocks exactly one tenancy.
  • Recovering interrupted purchases: on launch and on every CustomerInfo update, Keyback reconciles nonSubscriptionTransactions. A purchase interrupted between payment and saving (say the app is killed) is recovered and assigned to the unpaid tenancy.
  • Restore Purchases is on the pack screen.
  • Entitlement fallback: if the pack is set up in the dashboard as a non-consumable on an entitlement instead, the gateway honours that too, and a test pins each path.
  • No key in a build: the app still runs, the pack screen says purchases aren't set up, and nothing unlocks by accident.

All purchases go through a small PurchasesGateway interface with a fake for tests, so the unlock rules are tested without the store. The demo shows a real purchase through RevenueCat's Test Store.

Challenges I ran into

  • Every state starts the clock differently.
    • California and New York count from the day the tenant moves out.
    • Texas counts from a written forwarding address.
    • Arizona counts from the tenant's demand, in business days that skip Arizona's legal holidays.
    • Colorado uses "whichever occurs last".

Where a statute's wording leaves two possible dates, Keyback uses the later one and says so, so a renter never acts before the landlord is actually late.

  • England's "30 days beginning with". The day the deposit is received is day 1. That off-by-one is pinned by a test.
  • freeTSA rejected my first timestamp requests. My DER encoder wrote integers with a leading zero byte that DER doesn't allow, and freeTSA refuses them. Other servers accepted the same request. I fixed the encoder, then made real responses from all three servers into test fixtures.
  • Photos saved in quick succession collided on disk. Two writes to the same file used the same temporary name, and one rename failed. Writes are now queued per file, each with a unique temporary name.
  • A duplicate quote, found while recording the demo. In New York, the deadline and the forfeiture come from the same subsection, so one finding, and the letter, quoted it twice. The letter also said "the statute quoted below" for a charge whose statute wasn't there. Both are fixed, with tests that fail on the old behaviour.
  • RevenueCat Test Store keys only work in debug builds. A release build with a test key shows "Wrong API Key" and closes. The README explains this, and the release APK is built without a key.

Accomplishments that I'm proud of

  • Every deadline, cap and flag in the app can be traced to a quoted sentence of law with a source link and a check date.
  • A renter can verify any photo's timestamp with openssl and no trust in me or my app.
  • The free tier is genuinely enough to protect someone. The purchase is for convenience, not for their rights.
  • It shipped. Keyback went live on 10 August 2026 and has passed 1,000 downloads across the App Store and Google Play.

What I learned

  • Statutes are more precise than the advice written about them. "Within fourteen days after the tenant has vacated" and "30 days beginning with the date" behave differently, and only the original text settles it.
  • A good paywall decision is a product decision: who is paying, when, and what they are afraid of losing.
  • Honest limits make an app more trustworthy. Keyback says what it can't decide. For example, a timestamp proves a photo existed at a time, not what it shows.

What's next

  • Illinois and Massachusetts, once their statute text can be verified at the source.
  • Wales and Scotland, which have their own deposit schemes.
  • Move-out photo pairs taken with the move-in photo shown as a ghost overlay, so the angles match.

Built With

  • dart
  • digicert
  • flutter
  • freetsa
  • purchases-flutter
  • revenuecat
Share this project:

Updates

Submission history