Inspiration

When a lease or loan is about to end, a Toyota customer has a lot to weigh: buy it out, return it, refinance, pay extra, or trade early into a new lease. Each choice has a different five-year cost, and most people can't work that out on their own. An AI advisor makes it worse if it ever guesses a figure. I wanted one clear answer for the customer, and a model that is never allowed to supply a number.

What it does

Crossroads is an advisor for Toyota Financial Services customers, with a separate dashboard for Toyota staff.

A customer picks their account and sees their contract, their mileage trend with the projected overage, their money, and what the car is worth. Then they open Your paths, where every option shows its five-year cost, monthly payment and upfront cost. One is recommended, with a plain-language reason and a "Numbers verified" badge. Every other option says in one line why it lost, such as "Costs $2,310 more over 5 years". Options the customer can't afford stay on screen with the shortfall, because hiding bad news isn't advice.

They can move sliders for miles, budget, years and rate and watch the answer change, or ask a question like "what if I pay $500 more?". It ends with a next step and the three nearest Toyota dealers.

Staff sign in to a separate dashboard that shows retention, estimated five-year revenue, the path mix and customer choices as they come in. A fairness check asks whether the app steers lower-income customers toward paths that earn Toyota more.

How we built it

At the center is a Python engine that does all the money math with Decimal and has tests. Everything else either feeds it data or explains what it decided.

For the Toyota "What's Your Next Move?" track, the engine takes one customer and compares every option on their lease or loan over five years: total cost, monthly payment and upfront cash. It picks one, explains why in plain words, says why each other path lost, and shows the one input change that would flip the answer. Then it points to a next step: handle it yourself, take an eligible offer, or talk to a specialist. The staff dashboard shows what those choices are worth. Across our 12 synthetic customers, 83% are retained, estimated five-year revenue is $54,670, and 42% of recommendations need no specialist. Two customers can't afford any option, so the app shows their shortfall instead of recommending one. No language model writes a figure, and a separate fairness check scores 500 synthetic customers in three income bands and states its own limits.

For Capital One's Nessie track, we create each customer's accounts, bills, deposits, purchases and merchants in the real Nessie API and read them back. The balances, income and bills on the Overview are what Nessie returned, and the screen says so. We created 12 customers, 24 accounts, 72 bills, 36 deposits and 288 purchases, and every one read back correctly. A "Sync from my bank" button re-reads a customer live in about half a second.

For SerpApi, we use two of its APIs. Google Maps finds the three nearest Toyota dealers for the next step. Google search results give local used-car prices, which set each car's market value. The screen labels each one "Google results via SerpApi" or "Demo data", so a customer can tell which they're looking at. Results are cached in the database. On the live seed, all 10 cars got real quotes, and dealers loaded for all 6 ZIP codes.

For the Gemini track, Gemini writes each explanation with named placeholders instead of numbers. The engine fills them in, and a check confirms every figure matches before anything is shown. If the model is down or a figure fails the check, a checked template takes over. Gemini also answers the question box, so "what if I pay $500 more?" gets a reply built from engine results. All 94 live explanations we generated for the 12 customers passed the check, with a median of 1.4 seconds.

For Tiger Data, the data sits in three hypertables: odometer readings, payments and transactions. Three continuous aggregates feed the trend charts, and compression policies shrink older rows, grouped by customer. When a customer picks a path, the app writes a row to a fourth hypertable, and the staff dashboard shows it within a few seconds with no reload. In a scale test, 1,080,000 synthetic hourly readings shrank from 75.7 MB to 8.0 MB, a saving of 89%.

For Vultr, the whole stack runs on one instance. Caddy gets the HTTPS certificates for the customer and staff sites, and only ports 80 and 443 are open to the public. The database and both APIs are never published, the two apps sit on separate networks, and the staff API returns 401 to anyone who isn't signed in. One script copies the code over and rebuilds, and the server's secrets never leave the server. It's live at https://app.149.28.244.73.nip.io.

Challenges we ran into

The hardest part was making sure no number comes from the model, even when someone asks a leading question or tries to override its instructions.

The real Nessie API needed four fixes the docs never mention: https, a merchant, a status on deposits and purchases, and a string merchant category.

For the fairness check, I wanted one that can fail. It says "possible steering" on a deliberately rigged test population, so we know it isn't a check that always passes.

This was also my first time using Vultr to set up a cloud compute instance and deploy, which meant I had some difficulty deploying at first.

Accomplishments that we're proud of

94 live explanations across all 12 customers passed the number check, with a median of 1.4 seconds. I created 12 customers, 24 accounts, 72 bills, 36 deposits and 288 purchases in Nessie and read them back with no mismatches. In a scale test, 1,080,000 synthetic hourly readings shrank from 75.7 MB to 8.0 MB.

The fairness check found that the lowest-income band's recommendations rank 34.1 points lower in profitability to Toyota than the highest band's. That reads as "no evidence of steering", and the page states its own limits next to the verdict. The app is live on Vultr over HTTPS, and the staff API returns 401 to anyone who isn't signed in.

I'm also proud of being able to learn and implement a lot of different tools and frameworks for the first time, like FastAPI, TanStack Query, TimescaleDB, and Vultr. This was also my first time using SerpAPI, and my first time implementing Docker in a hackathon setting.

What we learned

  • A boundary the model can't cross is more dependable than a careful prompt
  • Giving every integration a fake or a fallback made our tests fast and the demo safe when a service was slow
  • A redesign can quietly break layout
  • Deploying is its own piece of work, with the server config, the seed step and the secrets each needing attention

What's next for Crossroads

  • Real customer sign-in, so each person sees only their own account
  • Real contract and payment data in place of the synthetic set
  • More options, including cash buyouts, which this doesn't model yet
  • A wider fairness check, since this one looks only at income
  • A hosted Tiger Data instance instead of our own container, and a proper domain instead of nip.io

Built With

Share this project:

Updates

Submission history