Five lenders, one operating picture.
How Necsen built a financier integration platform that normalizes five solar lenders and Aurora Solar into a single canonical model — ending the era of operations staff logging into a different portal for every deal.
- Client
- Atlas
- Sector
- Residential solar & financing
- Engagement
- Lender integration platform
- Scope
- 5 financiers + Aurora Solar · production on AWS
Every financier, translated into one language.
Atlas runs residential solar operations financed through third-party lenders — loans through GoodLeap, and lease/PPA programs through Palmetto's LightReach, EnFin, Concert, and Sunrun — with system design and proposals running on Aurora Solar. Each of those relationships came with its own portal, its own API, and its own vocabulary.
Necsen was engaged to build a dedicated integration service that sits between Atlas's systems and every lender. It translates each financier's API into one canonical model covering 19 business domains, keeps a local record fresh through webhooks and polling, and serves that record to two consumers: a purpose-built operations console, and Atlas's existing CRM through a read-only integration API.
The service runs in production on AWS, carrying more than 2,250 live project records — and adding the next lender requires no change to the console, the CRM, or any existing integration.
Every deal lived in someone else's portal.
Financed solar deals move through application, approval, contract, installation milestones, and funding — and every one of those steps happens inside the lender's systems, not Atlas's. With five lender relationships, the operational picture was fragmented across five portals that shared nothing.
A login for every lender
Operations staff kept separate portal credentials for every financier. Checking a single deal could mean signing in to GoodLeap, then Palmetto, then EnFin — and reconciling what each one said by hand.
Five APIs, five dialects
Each lender exposes a different API: REST with OAuth2 here, a Salesforce-backed TPO service there, quote-and-webhook flows somewhere else. Nothing shared a vocabulary, so nothing could be compared.
No answer to "what's blocking this deal?"
The questions operations asks every day — what happened, when, why, who acted, what happens next — had no single place to be answered. Every answer lived inside a different lender's portal.
A CRM that couldn't see financing
Atlas's existing CRM carried the project record but had no view of loan status, funding milestones, or contract state. Building a bespoke CRM integration per lender would multiply the problem, not solve it.
Stale by the time it was read
Status was gathered manually, so it aged the moment it was written down. Milestone changes at the lender could sit unnoticed for days.
No record of lender interactions
There was no audit trail of what was requested from a financier, when, or what came back — a gap that matters when funding disputes come down to who knew what, when.
Specification first, then five workstreams in parallel.
Integration work lives or dies on the quality of the contract. The engagement front-loaded the specification so the lender builds could run independently without colliding.
A functional specification before any code
The engagement began with the lender APIs themselves — official API documents, field tables, and per-state rules — distilled into a functional specification covering 19 business domains, from application and contract through funding and milestones.
API analysis · 19-domain specification · Field-level source mappings
One canonical model, adapters at the edge
The core design decision: Atlas's own systems never talk to a lender directly. Every financier is wrapped in an adapter that translates its API into one shared canonical model, so all provider complexity stays at the edge.
Canonical data model · Provider interface · Shared OAuth transport
Five lenders built as parallel workstreams
Because every adapter implements the same interface, the lender integrations were built as independent parallel workstreams — GoodLeap, Palmetto, EnFin, Concert, and Sunrun — alongside the Aurora Solar design-data integration and the operations console.
5 lender adapters · Aurora integration · Operations console
Hardened, audited, and cut over live
The service moved to dedicated AWS infrastructure with a fixed IP — the prerequisite for lender allowlisting — followed by security hardening, a full evidence-based production audit, and a live cutover that synced thousands of real accounts.
AWS deployment · Security hardening · Production audit & cutover
One engine, one console, one contract.
The platform has three faces: the integration engine that talks to lenders, the console operations works in, and the API that feeds Atlas's CRM. All three speak the same canonical model.
Integration engine
The backend that speaks every lender's language so nothing else has to. Each financier is a self-contained adapter behind one shared interface — adding a lender is a new adapter, not a new project.
- Adapters for GoodLeap, Palmetto, EnFin, Concert, and Sunrun
- One canonical model spanning 19 business domains
- Shared OAuth transport handling 6 different auth styles
- Rate-limit handling, timeouts, and safe retry rules
- Credential store with per-lender live/demo modes
- Aurora Solar design and proposal data, kept alongside
Operations console
A single console that replaces the lender portals for daily work. A cross-lender pipeline up top, a dedicated section per financier beneath it, and a full deal view that answers the operational questions in one screen.
- Cross-lender projects pipeline with live sync
- Deal detail across overview, applicant, financials, and docs
- Per-lender portals mirroring each financier's own views
- Credit application origination submitted from the console
- Unified audit trail of every event and API call
- Credential and connection management per lender
CRM integration API
A deliberately small, read-only API that feeds financing data into Atlas's existing CRM in the canonical shape — so the CRM needs zero lender-specific code, today or for any financier added later.
- Provider-neutral responses in one versioned schema
- Project matching, retrieval, and on-demand refresh
- Read-only by design — the CRM can never mutate lender state
- Missing data surfaced as unavailable, never silently zeroed
Six systems, none of them alike.
The hard part of financier integration is that no two lenders agree on anything — not authentication, not data shape, not how updates are delivered. The platform absorbs those differences at the edge so everything downstream sees one consistent system.
The integrations
- GoodLeap. Point-of-sale solar loans over REST with OAuth2 — applications, loan status, and milestones normalized into the canonical model.
- Palmetto · LightReach. Third-party-ownership leases via quote APIs and real-time webhooks. The production cutover synced 2,150+ live accounts, with webhook deliveries flowing at volume from day one.
- EnFin. A Salesforce-backed TPO program. The credit-application mapping was verified field by field against EnFin's official API document, including per-state requirements.
- Concert. TPO customers, funding, and payment schedules, brought into the same pipeline as every other lender.
- Sunrun. Salesforce service-contract data for TPO deals, integrated in demo mode until Sunrun exposes a partner data feed.
- Aurora Solar. The design-and-proposal source of truth — projects, system designs, pricing, financings, and web proposals — kept deliberately separate from the lender registry.
One shared transport underneath
Every adapter rides on the same hardened HTTP layer, so reliability and security are solved once — not re-solved per lender.
- One OAuth client covering 6 distinct lender auth styles
- Retries restricted to safe reads — writes are never replayed
- Rate-limit backoff that honors each lender's signals
- Per-organization token caching with early refresh
- Secrets redacted from every logged request
- Every outbound lender call recorded in an audit log
Data that stays fresh on its own.
Nobody at Atlas refreshes lender data by hand. Whether a financier pushes webhooks or has to be polled, every update flows through the same three steps.
Normalize
Updates arrive however the lender offers them — webhooks where they exist, a background poller where they don't. Every update is translated into the canonical model at the edge.
Record
Each change is written as a project snapshot plus an append-only event, deduplicated and idempotent — the same update arriving twice can never corrupt the record.
Serve
The console and the CRM read from the same local store, so downstream consumers can't tell — and never need to care — whether an update arrived by push or by poll.
What changed in the business.
The figures below are the footprint of the engagement. The items that follow are the operational changes Atlas runs on today.
Lender portals retired from daily work
The engagement's stated goal — eliminate the need for operations to log into financier portals — is served by one console that answers what happened, who acted, and what's next.
Freshness without chasing
Webhooks and polling keep the local record current automatically. In the first day after the Palmetto production cutover, hundreds of webhook deliveries were accepted and applied without manual work.
New lenders without new projects
Adding a financier means implementing one interface — not touching the console, the CRM, or any other integration. The fifth lender was added the same way as the second.
A CRM that finally sees financing
The read-only integration API feeds canonical financing data into Atlas's CRM with no lender-specific code on the CRM side — the same contract covers every current and future financier.
A defensible audit trail
Every event and every outbound lender call is recorded, so operators can see exactly which API interactions produced the data on screen.
Production in under two weeks
The service went from first commit to a hardened AWS production deployment in eleven days, then scaled through lender-by-lender cutovers as each financier granted access.
Why the engagement worked.
A five-lender integration platform succeeds or fails on architecture and verification discipline. Three decisions did most of the work.
The model came before the integrations
Designing the canonical model and provider interface first meant five lender integrations could proceed in parallel without colliding — and meant the CRM contract was stable before a single lender went live.
Verified against the documents, not assumptions
Every mapping was checked against the lenders' official API specifications — down to per-state required fields — and the production audit demanded file-level or live evidence for every claim before cutover.
Honest status, lender by lender
Each connector carried an explicit status — live, ready, blocked, or demo — reported to the client weekly. When a lender-side grant was the blocker, that was stated plainly rather than hidden behind demo data.
We used to keep five lender portals open in browser tabs all day. Now the team works from one console, and our CRM finally shows financing status without anyone copying it in by hand.
The practices behind this engagement.
Three of Necsen's service lines carried this work. Each page describes the practice in full.
API development
The core of this engagement: integrating five external lender APIs behind one canonical contract, with a versioned API serving downstream consumers.
Business workflow automation
The sync engine's discipline — webhooks, polling, and an event log that keeps records current without anyone chasing status.
CRM development
The integration API that feeds financing data into Atlas's CRM — provider-neutral, read-only, and built to outlast any single lender relationship.
Bring us the systems that don't talk to each other.
We design and build integration platforms, APIs, and business systems that turn a tangle of third-party services into one coherent operating picture. No commitment. No pitch deck. Just a technical conversation.

