This project is password protected
Enter the password to view this case study.
Incorrect password
Building TPO Financing from Zero
One coherent experience across three financiers
For Aurora Solar
Research, problem definition, design principles, design iterations, API analysis, third-party and cross-domain collaboration, finalized design, handoff, success measurement, post-release refinements
Sales rep, Admin, Homeowner
Sole designer; financing and Sales Mode pods plus three external financier partners
Release 2023 · Refinements through 2025
Background
For years, most residential solar in the US ran on loans and cash. As rates climbed and incentives shifted, the market moved toward third-party ownership (TPO), where a financier owns the rooftop system and the homeowner pays for the energy it produces. Aurora’s customers were losing deals because the platform couldn’t offer it, so leadership made TPO the company’s top priority for several quarters running.
TPO is not simply a third financing type; it is modeled, priced, and governed differently than a loan. With a loan or cash deal, pricing is built up: system cost plus adders, storage, discounts, optimizations, and incentives, and the homeowner pays the sum. With TPO, the financier owns the system: we send them the design (yield, panels, address, utility) and they return what it costs to move forward. Because they own the asset, they are far more prescriptive about what goes on the roof and what they will fund.
Problem
We weren’t integrating one financier. We were integrating three, EnFin, LightReach, and GoodLeap, with more to follow. Each modeled TPO differently: its own data, thresholds, workflows, and requirements across a project’s life.
So the real problem: establish an entirely new financing model while absorbing three financiers’ worth of structural inconsistency behind one experience that holds together throughout design and sale, regardless of financier.
Principles
With a project as complex and wide as TPO, a set of unified principles kept the team on track and kept our conversations grounded.
Default to consistency
One model, one flow, one mental picture for the rep, regardless of financier.
Selectively bespoke
Support functionality that's required for MVP but keep it narrow.
Generalize
Evolve bespoke features so they can scale to other financiers.
Phase one
Modeling TPO
We started with Aurora’s custom financing products while the third-party partnerships were still being signed. This is the part of the platform where customers model and offer their own financing (custom loans, leases, and the like). It was where I learned how differently TPO behaves.
I took Aurora’s existing loan selection flow to a financier who was also a customer and had them mark up everything that didn’t fit a TPO. The first thing I learned: TPO comes in two flavors, a PPA (power purchase agreement) and a lease. In both, the homeowner never owns the system and pays the financier over time. With a PPA they pay for the performance of the system (per kWh), where leases tend to be levelized payments. Which form is offered can also vary by state.
Three other things stood out:
- Selection. For a loan, the rep picks a product. For TPO, the rep chooses among rate and monthly-payment options, so selection needs a decision step loans never had.
- The financing card. The information shown when a product is selected, in both Sales Mode and the proposal, was loan-shaped and wrong for a TPO.
- The model. TPOs are driven by yield, solar rate, and EPC PPW, not by a built-up cost.
The hardest part was not visual. It was modeling a new financing object in the database in a way that wouldn’t paint us into a corner later. I worked through several concepts with engineering before we committed.
Customer feedback in higher fidelity sharpened a few things: reps needed to switch between TPO escalators directly from the rate modal; they needed the pre-solar rate visible in that modal because it is the number they anchor on (the solar rate they pick has to come in under it); and EPC PPW had to be hidden from the homeowner-facing view, since it is a rep-only number.
Phase two
Integrating the financiers
The shared layers
Onboarding a partner to Aurora and to the integration methodology was the first step once contracts were signed. It surfaced open questions, highlighted where data needed to sync between the two systems, gave partners a high-level view of the Aurora flow, and let us start teasing out the feature list.
From there, I led discovery on the foundation all three integrations needed: how an integration gets enabled for an organization, the financier-specific configurations, the disclaimers that surface on the proposal, and error standards.
Error standards mattered more than they sound. A bad error from a financier’s API becomes a dead end for a rep at a kitchen table. So I worked with the financiers directly, recommending best practices: what to say, when to say it, and how to make an error recoverable rather than terminal. That meant shaping partner behavior upstream, not just designing Aurora’s surface, so the experience held together no matter which financier threw the error.
A robust component
As the integrations grew, the team took on a front-end refactor for a cleaner, more scalable foundation. To support it, I built a robust financing-card component in Figma that captured every state the card could be in across the financing journey. It became the shared language for how the experience behaved, financier by financier, state by state.
Bespoke features for MVP
Adders (LightReach, then GoodLeap)
LightReach only allows a small subset of adders on a TPO, categorized as electrical upgrades necessary for installation, plus only certain storage operating modes. Both are regionally specific (not every option is available in every state) and both change TPO pricing. I introduced a modal for the rep to make these selections at the point of choosing a LightReach TPO.
GoodLeap had a related but distinct constraint: any adder had to fall within GoodLeap’s approved categorizations, though GoodLeap’s TPO price doesn’t change with adders. I shipped a categorization config for that.
Two financiers, two adder problems, one underlying shape. I proposed a single tagging system that could serve both cases instead of accreting one-offs. It was the right long-term answer, and I advocated for it, but it did not get prioritized before I left. The MVP shipped the per-financier solutions; the generalized version stayed the recommendation. (More on that in the learnings.)
Licensing (LightReach)
To select a LightReach TPO, reps need to enter a sales license number, but only in five states (CA, CT, IL, MD, NV). The immediate solution was a modal to capture it at the point of selection. There was a heavier, “proper” solution: managing licenses as a config on the user object. I argued to deprioritize it, for reasons that were really about resourcing:
- We would still need the modal regardless, for reps selecting LightReach without a stored license, so the modal was the durable building block either way.
- The config was a materially heavier lift that would push MVP.
- It benefited exactly one financier (for now).
- It wasn’t required on every project, only in five states.
So we shipped the modal and backlogged the config improvement.
Change orders (LightReach)
Once contracts are sent or signed, any design change has to go back to LightReach for approval. I designed a change-order sequence: after documents are sent, a control appears on the financing card; triggering it calls LightReach’s API and resolves to one of three outcomes (approved, previous documents voided and reissued, or rejected).
A couple of quarters later, this same sequence was rolled out for GoodLeap (respecting their own change-order logic).
Partner readiness (EnFin and GoodLeap)
EnFin was building their APIs and financial modeling while we integrated, so much of that MVP was coordination just to get the integration standing before any unique work could begin. GoodLeap’s TPO credit-application iframe wasn’t going to be ready for launch, so Aurora designed and built a credit-app page from scratch to hold the release date, with the explicit plan to sunset it once GoodLeap’s was live. That temporary page becomes important later.
MVP release
After a six-month design and build phase, we released pilot programs for TPO, then quickly moved to general availability in Q3 2024. It was adopted by 4 of the 5 highest-volume Aurora customers.
Phase three
Resolving errors
High usage surfaced a wave of errors. Before fixing anything, I stood up an error report in Mode (with help from engineering and product partners) so we could see what was actually breaking and how often, then combined it with themed findings from the support team.
A few of the issues and how we fixed them:
Missing homeowner info
Financiers need basic homeowner details to run a credit application, and reps were routinely hitting “apply” before those details existed, which produced errors. I added a validation step on apply: if name, email, or phone is missing, a modal captures them before the application opens.
The address bug
GoodLeap-specific: a project’s address in Aurora was being normalized by Google’s API but failing in GoodLeap’s system, which used USPS. I started with the address team, who confirmed Google was better equipped than USPS and also handled international projects, so changing Aurora’s validation was off the table.
I considered letting reps manually overwrite the address by expanding the homeowner-info modal.
Why this doesn’t work
- ❌It created another address object in Aurora and complicated the address 'source of truth.'
- ❌It still wasn't guaranteed to pass GoodLeap's USPS check, so it wouldn't fix the errors.
Then I noticed something. GoodLeap’s TPO credit application, the iframe we’d built the temporary page to stand in for, was now live, and it contained an editable address input (the same input loans had always used, which is exactly why loans never hit this bug). So I asked GoodLeap directly: if we use your iframed application as originally intended, does the problem go away? Yes.
Why this works
- ✅No new feature, and no new address object to maintain.
- ✅It used a partner surface that was already live and already correct.
- ✅It revived a tech-debt item we'd always wanted.
My recommendation was to build nothing new, retire our temporary page, and bring the original iframe plan back off the backlog. It closed the GoodLeap loop that started at launch.
Performance simulation
Each financier requires specific performance-simulation settings (weather data, shading engine, and so on) for a rep to offer their product; misaligned settings produce wrong numbers and errors. Aurora’s structure assumed one design equals one set of perf settings, controlled at the org level and adjustable per design by some users.
I made financier settings drive the sim during a TPO: a toast tells the rep their perf settings changed when they select a TPO, settings revert to the org defaults when TPO is deselected, and I disabled the per-design panel during the TPO workflow, since users editing values we were auto-writing from a financier’s API would just reintroduce drift. We recommended financiers send these via API to stay in sync, and supported hardcoded values as an interim.
This one looked small and was technically heavy, and it surfaced a real disagreement: the design-domain pod felt we shouldn’t hand Aurora’s accuracy to a financier whose goals might differ from the installer’s. I held that in a TPO the financier owns the system, so their settings are the source of truth. That position carried.
Cancel flow
One smaller fix rounded it out: letting reps cancel an approved GoodLeap product so they could switch financing types without hitting an error.
Consolidating situational actions
By this point the financing card carried many different permutations and state changes that it was getting difficult to track. I prototyped a more-options menu (built with AI to move fast) that exposed the same set of actions for every financier and enabled or disabled them based on where a project sat in its financing journey. Timestamps added clarity on which actions had already been taken for a financing product. The team liked it. It is where I’d take the system next.
Results of the error work
Across the full error effort, projects with errors fell from 16,886 to 3,827, a reduction of more than 75%.* The AVL compliance work was the largest single contributor and is broken out as its own case study.
*This figure reflects the entire error-reduction program, not any one feature.
Phase four
TPO pricing parity
One gap we’d knowingly deferred at launch was pricing parity. For loans and cash, pricing is built up from adders, storage, discounts, and energy optimizations. For TPO, those add-ons had no effect on the core pricing datapoints, so a rep adding equipment costs (scaffolding for a three-story roof, say) couldn’t see those costs reflected in the TPO price. Reps were doing mental math to land on a number that covered the real project and still hit their margin. It was a known issue from TPO’s inception; we’d held it because the integrations came first and the untangling was genuinely hard.
The solution introduced an add-ons PPW (inclusive of adders, discounts, storage, and energy optimizations) and a base PPW derived by subtracting it from the gross PPW the financier returns. Because base PPW is already how loans and cash work, populating it for TPO brought the two into one mental model and made downstream values, including PPW range enforcement, behave correctly. The work touched many areas of the platform, including the calculations underneath.
I gathered feedback on the designs two ways, live sessions and recorded Loom walkthroughs, to reach customers at scale. The response was strongly positive.
The instructive part came at release. Customers loved it. The financiers panicked. Enough time had passed since they’d signed off on the design that EnFin, GoodLeap, and LightReach all assumed the feature was adding costs on top of their payment, which would mean paying out more. It took a round of calls, including some leadership teams, to walk each one through what the feature actually did. Once explained, there was no issue. That gap, between sign-off and release, is the thing I’d design around next time.
Phase five
The reality of legislative volatility
Partway through TPO’s maturity, the ground shifted under the entire industry. The One Big Beautiful Bill Act eliminated the 25D Residential Clean Energy Credit for systems installed after the end of 2025, removing the 30% incentive that residential solar had been built around. The fallout was real, and the partner side wasn’t spared. Customers went out of business. Of the new TPO financiers we had in active development, two folded outright, one right before a beta and one just before its TPO product shipped, and a third paused work mid-build.
I led discovery on Aurora’s financing response: untangling what the legislation actually required, then mapping how the platform’s ecosystem (incentive defaults, pricing, financing options) needed to change so customer numbers stayed accurate and compliant against a hard, non-negotiable deadline. The work ran cross-pod with engineering and product to ship in time.
Results
TPO generated $2M in first-year usage value* and $12M cumulatively. By Q4 2025, it had compounded into a core part of how Aurora’s customers sell.
*Usage value is Aurora’s term for the dollar amount of customer activity transacted on the platform.
“Aurora has reduced change orders from 40% to less than 10%, saved us up to 72 hours per job, and enabled us to scale TPO volumes without increasing operational strain. It’s a game-changer for our growth.”
— Our World Energy
Learnings
- Sometimes the best design is no new design. Resurrecting a buried backlog item beat building a new feature. Restraint is a design skill.
- Principles are load-bearing at this scale. They kept hundreds of small decisions coherent without re-litigating each one.
- The devil is in the details. Financiers are intensely data-centric, so misrepresented costs or mishandled data syncs become real problems fast.