Collins Kiptoo
Case study: Cloover

Quote to payout. Every step.

The backend of a lending platform for solar, batteries and heat pumps: how an installer's offer becomes a checked, signed, financed loan, how the platform talks to the 30-plus services it depends on, and how the money and the audit trail follow. Two and a half years of this at Cloover, a Berlin climate fintech backed by a $1.2B debt facility and a €300M European Investment Fund guarantee.

The journey

From an installer's offer to a financed customer

Every loan on the platform starts as an offer an installer builds for a household and ends with the installer being paid. I worked on every step in between, and on how they hand off to each other.

Offers and checkout. The rules that turn an installer's quote into something a customer can finance: line items with the right VAT (solar and batteries at 0%, heat pumps at 19%), split projects where one parent becomes a solar loan and a heat-pump loan, status rules for checkout, and a lock so an offer edited after the contract is signed can't slip through to payment. Income-verification guidance on the upload step. A Python check that every split sums back to its parent, which caught a double-click that split a project twice.

The credit decision. Schufa for Germany and CRIF for Austria, processed into structured underwriting data. The mapping from bureau score to probability of default that the rest of the platform relies on. An affordability rule on existing debt against net income. Underwriting alerts to the credit team with the numbers they need, and a backoffice page to import a report by hand when the API can't be used.

Contracts, invoices and the money. DocuGenerate contracts and invoices with country-specific tax footers, Postmark emails at every step, then SEPA direct debit, Stripe and Buckaroo for the payments themselves and payouts to installers.

Testing it without touching real people. For the Checkout 2.0 rebuild I built the test users, mock bureau data so tests never call the live Schufa service, documentation of every validation message, and an audit of where old credit-check, KYC and approval statuses were still used across the platform. I was the named validator on fixes before they shipped.

OFFER OF-2291
solar, battery, heat pump
Solar PV, 9.8 kWp0% VAT·✓
Battery, 10 kWh0% VAT·✓
Heat pump19% VAT·✓
Credit checkSchufa·✓
Affordability rulepassed·✓
Contractsigned·✓
StatusFinanced
Illustrative offer

Also in the journey

  • Accounts created only after the customer signs, with no password step for cash payers
  • Organisation-level feature switches, so markets and partners could get different flows
  • An installer incentive programme: monthly tiers, milestone bonuses, credit notes and invoicing, which I inherited and maintained
  • Lead notifications to installers, with a PDF dossier per lead
  • Solar and address data in the offer flow: roof insights, geocoding, address autocomplete
The integrations

30-plus integrations, one API layer

More than 340 calls across 30-plus integrations: about 25 outside services plus the platform's own internal and partner APIs, built and maintained through the API connector rather than plugins. The count next to each name is the number of calls in that integration.

Payments and bankingStripe (intents, Checkout, SEPA, payouts, identity, verification), Buckaroo, Nordiska, PAIR Finance, QuickBooks, BIC lookup, currency exchange
Credit and complianceSchufa, CRIF, OpenSanctions screening, Zerobounce email validation
Documents and messagingDocuGenerate, Postmark (including signed webhooks), ConvertAPI, Clicksend SMS, Slack
CRM and partnersHubSpot, Zapier and Pipedrive (approved for Zapier's public listing), a partner API
Solar and locationGoogle Solar insights, a solar-yield API, Google Places, OpenCage, country data
Platform and AIBubble Data and Bulk APIs, internal utilities, the new Google Cloud checkout, OpenAI and the platform's own AI features

Keeping it safe. Privacy rules across core data types; webhook authentication for Stripe, Buckaroo and Postmark; a hardcoded secret found in a partner webhook and rotated; live keys removed from the test environment; unknown records now stop a webhook instead of acting on it.

Bubble Data API115Postmark81HubSpot28DocuGenerate25Stripe17Internal Utils11Nordiska10QuickBooks9Postmark signatures5GCP Checkout4Solar API Insights4Buckaroo3Stripe verification3Bubble Bulk API3AI Features API3Schufa2OpenSanctions2PAIR Finance2ConvertAPI2Google Places2Math.js2Google Solar1OpenCage1OpenAI1Zerobounce1Clicksend1Currency Exchange1BIC lookup1Country data1Stripe Identity1Slack1Partners API1
Compliance

EIF guarantee eligibility

Why it exists. The European Investment Fund guarantees part of the loan book. Auditors need to see, per deal, why it was eligible: financing period, borrower risk, effective rate against a cap, margin over the cost of funds, a transfer-of-benefit test. That evidence has to survive later changes to rates and tables.

What I built. Version one encoded three conditions on the credit application. Version two, scoped with the finance owner and the platform and data leads, gave eligibility its own record: one row per project, created automatically at approval, serving both the legacy platform and the new one. A frozen refinancing bank and cost-of-funds snapshot. Four stored checks. The derived inputs. Recompute metadata, so any later recalculation is itself auditable. A tab on the backoffice project page where an auditor can read it.

Before building it, I insisted on the finance team's real spreadsheet rather than a CSV of its outputs, because formulas can't be reconstructed from results, and audited it cell by cell. A cost-of-funds mismatch between the worked example and the lookup table (6.90% against 6.35%). A risk-rating tier missing from the parameters. An ambiguous definition of capital at risk. All three fixed before sign-off.

Backfill. Every approved German project, split projects taking their score from the parent, 122 live deals sent to finance for verification. The first live test treated a missing snapshot as zero and inflated the margin; I caught it, fixed it, and confirmed two deals correctly came back ineligible because flat-rate pricing breached the maximum margin.

CountryPASS
Transfer of financial benefitPASS
Margin over cost of fundsPASS · COF 6.35%
OverallELIGIBLE
one record per deal, frozen at approval

Credit data

  • Schufa and CRIF reports processed into structured underwriting data; the score-to-probability-of-default mapping used across automations
  • Mock bureau data so the test environment never calls the live service
  • Underwriting notifications extended with revenue and a refinancing-premium calculation
  • A backoffice page to import credit reports directly
  • 63 projects with missing credit records reconciled for the data team; 21 traced to a checkout bug on the new platform

Invoices, fees and the arithmetic of VAT

Most of this is forensic: sweep every live project, find where money is wrong, batch it so finance can act, fix the cause.

  • Fee-inflation sweep across all live projects: seven fix batches with CSVs. Batch one alone held 434 records with a flag set and no fee.
  • Transaction fee enforced but never passed to the customer on 89 projects; an automation fixed the 50 where it was never calculated.
  • Missing pricing on seven projects, one already invoiced short; all 45 split projects since March checked.
  • Effective interest rate annualised by multiplying the monthly rate by twelve instead of compounding. Fixed. It mattered whenever a loan was split.
  • VAT arithmetic: a field converted to net by multiplying by 0.81 instead of dividing by 1.19; a tax-invoice workflow adding a stale development fee.
  • Duplicate invoices traced to manual issue clicks inside a seven-day window; 33 credit notes linked back to their parent invoices.

Documents, email, dunning

  • DocuGenerate contract and invoice templates with conditional rendering and country-specific tax logic
  • Postmark transactional templates; bounce alerting restored after it had been silently dead for months following a key rotation
  • Swedish dunning: a five-point technical review for restarting reminders, plus the email and SMS copy packs finance signed off
  • Reminder ladders across Germany, the Netherlands and Sweden
Payments

Payout reconciliation

The problem. Every daily bank run, about 10 to 20% of incoming payments failed to link to an invoice. Finance matched them by hand, up to two hours per run, and the unmatched pile had grown to 532 payouts over seven months.

What I did. Opened the issue across engineering, finance and data. Traced the causes: payment intents cancelled after fees were added, money sent to the wrong virtual IBAN, under and overpayments, and a class of principal-repayment invoices the matcher never expected. Added traceability to every payout record so each link could be explained. Shipped the fix. Backfilled all 720 records in scope.

Result. Unmatched payouts fell from 532 to 157, about 70%. What remains is the set that genuinely needs a human.

What I'd do differently. Add the traceability first. Most of the diagnosis time went into reconstructing why a link failed after the fact.

DAILY BANK RUN
Stripe payouts to invoices
PO-481121,240.00openlinked
PO-48117 · wrong vIBAN612.00openlinked
PO-48120 · fee added2,105.30openlinked
PO-48124 · partial845.00openlinked
PO-481295,300.00openlinked
532 unmatched157 unmatched
Illustrative rows. Totals are real.

Also on the money path

  • Stripe PaymentIntents, SetupIntents, Checkout, SEPA Direct Debit, customer balance and virtual IBAN flows, payouts and refunds
  • The monthly direct-debit cycle: invoice on the 15th, customer notified, IBAN charged on the 5th, reconciliation by webhook
  • A refund confirmation arriving on the wrong webhook and flipping refunded invoices back to paid, diagnosed and fixed
  • Buckaroo mandates: errors traced to imported deals with empty references; a hotfix restored after an older change overwrote it
  • 133 direct-debit consents audited: 71 cleared, 8 with invalid bank data, the rest kept as legal evidence

Got something to build?

collo060@gmail.com
Collins KiptooNairobi, on European hours.
Contract, full-time, or a short paid pilot.