Technical Case Study
12 min read

How we built a compliance platform from scratch

The engineering decisions behind CapitalBridge: a portfolio compliance system handling 7 funds, 191+ borrowers, and 27 reporting types across 23 countries.

By Proceptio Engineering April 2026
The Problem

What DFIs were doing before

Most development finance institutions track compliance in Excel. Covenant thresholds checked manually. Submissions tracked via email. No borrower visibility into what is due, what has been received, or what is overdue.

When you manage a small portfolio of 10 or 15 borrowers, spreadsheets work. An analyst maintains a workbook per fund, tracks due dates in one tab, logs received documents in another, and checks covenant thresholds by hand each quarter. The process is labor-intensive but manageable. The problems begin when the portfolio grows past 50 borrowers across multiple funds, each with different reporting requirements, different covenant thresholds, and different currencies.

At that scale, the spreadsheet workflow creates four specific failure modes that compound over time.

01

Covenant breaches discovered after the fact

An analyst checks DSCR thresholds during the quarterly review. If a borrower breached two months ago, the lender has lost the intervention window. There is no early warning, no trend tracking, no "at risk" indicator before the actual breach occurs.

02

No audit trail for regulators

When was a document received? Who approved it? Was it on time or late? In a spreadsheet, the answer is whatever the cell currently says. There is no history. If someone overwrites a date, that information is gone permanently.

03

Manual FX conversion errors

A borrower in Zambia reports in ZMW. The fund reports in USD. Someone looks up the exchange rate, pastes it into a reference table, and hopes the VLOOKUP still points to the right cell. Rates go stale. Frontier market currencies require specialized data sources.

04

15-20 hours per week on email chasing

Checking the tracker, identifying overdue items, composing individual reminder emails, following up on follow-ups. Two full-time staff members spending the majority of each quarter on administrative overhead instead of analysis.

file_explorer.exe
47 unread emails
xlsx AATIF_Borrower_Covenants_Q3_FINAL_v7.xlsx 3 breaches
xlsx FMO_Reporting_Tracker_2024_UPDATED.xlsx 12 overdue
xlsx ALCBF_Compliance_Dashboard_ACTUAL_FINAL(2).xlsx conflicting
xlsx OGF_FX_Rates_Manual_Lookup_Mar24.xlsx stale rates
xlsx Covenant_Thresholds_ALL_FUNDS_JK_edits.xlsx v3 or v4?

A reconstruction of the compliance tracking state before CapitalBridge.

This is the environment we walked into. Seven funds, each with its own spreadsheet ecosystem. No single source of truth. No automated reminders. No borrower-facing portal. No real-time covenant monitoring. The compliance team was spending more time on administrative overhead than on actual portfolio analysis. Something purpose-built was needed.

Architecture

The cascading rules engine

The single hardest design problem in the entire system. Every reporting requirement and every covenant threshold needs to cascade through four levels of scope. Getting this wrong would mean reconfiguring hundreds of assignments by hand.

Why cascading scope matters

A fund like AATIF has dozens of borrowers spread across multiple sectors (Agriculture, Financial Institutions, Trade Finance) and sub-sectors. Each sector has different reporting needs. Financial Institutions need PAR30 and PAR90 reports. Direct Investment Companies need DSCR calculations. But within those groups, individual borrowers may have negotiated exemptions or additional requirements.

The naive approach is to assign everything individually: for each borrower, specify every reporting requirement and every covenant threshold. With 191 borrowers and 27 reporting types, that is 5,157 individual assignments to configure and maintain. When a fund-level policy changes, someone has to update every single one.

The cascading system solves this. You set defaults at the Fund level (scope 0). Those flow down automatically to every borrower in that fund. If the Agriculture sector needs different requirements, you override at the Sector level (scope 1). That override applies to all borrowers in that sector, but does not affect borrowers in other sectors. Sub-sector overrides (scope 2) work the same way.

At the bottom, individual Borrower assignments (scope 3) have final say. The most specific scope always wins. Setting IsActive = false at the Borrower level excludes that borrower from a requirement, even if the Fund, Sector, and SubSector levels all say it should apply. This is how exemptions work.

Implementation detail

Both ReportingRequirementAssignment and CovenantAssignment use the same cascading scope pattern. The resolution algorithm is identical: collect all assignments for a given borrower, group by requirement type, pick the one with the highest scope number (most specific). This means you configure the rules once and both reporting submissions and covenant monitoring inherit the same hierarchy.

scope_resolution.rs
Scope 0: Fund AATIF
AFS QMA CC ESAP SEMR +9 more
Scope 1: Sector Financial Institutions
+PAR30 +PAR90 +CAR
Scope 2: SubSector Microfinance
+MFI_KPI
Scope 3: Borrower Umoja MFI Ltd
-ESAP (exempt)
RESOLVED: AFS, QMA, CC, SEMR, PAR30, PAR90, CAR, MFI_KPI
EXCLUDED: ESAP (Borrower scope override, IsActive=false)

Most specific scope wins. Borrower-level exclusions override everything above.

Engineering

The hard problems we solved

Beyond the cascading rules engine, four specific technical challenges required careful design. Each one looks simple in isolation but becomes complex when you need it to work across 7 funds, 191 borrowers, and 23 countries.

01

Multi-currency at scale

CapitalBridge supports 156 currencies, including frontier market currencies that most FX data providers do not cover well: Zambian Kwacha (ZMW), Mozambican Metical (MZN), Rwandan Franc (RWF), Sierra Leonean Leone (SLE), Eswatini Lilangeni (SZL). These are not exotic edge cases. They are the primary operating currencies of borrowers in the portfolio.

The system syncs both spot rates and forward rates daily via a Hangfire background job. Every financial figure submitted by a borrower is stored in its original local currency. When a fund manager views the portfolio dashboard, all values are converted to the fund's base currency (typically USD or EUR) using the rate that was current on the submission date. Covenant thresholds are evaluated in the borrower's local currency, then the results are presented in the fund's reporting currency for aggregated views.

This dual-currency approach is critical. A covenant threshold of ZMW 5,000,000 means something specific in Zambian Kwacha. Converting that threshold to USD for evaluation would introduce FX risk into the compliance calculation itself. The system keeps thresholds in local currency, evaluates compliance locally, and only converts for reporting purposes.

fx_sync_daily.log live
ZMW Zambian Kwacha 26.42
MZN Moz. Metical 63.85
RWF Rwandan Franc 1,382.50
KES Kenyan Shilling 129.10
NGN Nigerian Naira 1,574.25
SZL Lilangeni 18.31
Base: USD | Updated: 2026-04-09 06:00 UTC
156 pairs synced | Spot + 30/60/90d forward
02

27 reporting types, not a generic document tracker

A common mistake in compliance software is treating all documents the same: upload a file, attach it to a borrower, mark it as received. That approach ignores the reality of development finance reporting, where each document type has its own frequency pattern, due date calculation, assignment rules, and validation requirements.

Annual Financial Statements (AFS) are due once per year, typically 90 to 180 days after fiscal year-end. Quarterly Management Accounts (QMA) are due every quarter, within 45 days. Portfolio at Risk 30 (PAR30) is a monthly metric specific to financial institution borrowers. Environmental and Social Action Plans (ESAP) have their own cadence. Each type was seeded into the system with its default frequency, due date offset, and applicable borrower categories.

The system distinguishes between two fundamentally different submission types. A ReportingSubmission is a document upload: the borrower uploads a PDF or Excel file. A FormSubmission is structured data entry: the borrower fills in financial metrics through a Blazor-powered form with field-level validation. Both types share the same status workflow (Pending, Submitted, Approved, Rejected, ReworkRequired) but serve different purposes.

A daily Hangfire job (CreatePendingReportingSubmissionsJob) looks 90 days ahead and automatically creates pending submissions for every active assignment. When a new borrower is added to a fund, the cascading rules engine resolves their requirements, and the job creates their pending submissions on the next run. No manual setup needed.

reporting_types.config
AFS Annual Financial Statements Annual
QMA Quarterly Mgmt Accounts Quarterly
CC Compliance Certificate Quarterly
FCC Fin. Covenant Calcs Quarterly
ESAP ES Action Plan Quarterly
PAR30 Portfolio at Risk 30 Monthly
PAR90 Portfolio at Risk 90 Quarterly
SEMR Social & Env. Monitoring Annual
SEIR Social & Env. Impact Annual
OR Operational Report Quarterly
DSRA DSRA Certificate Quarterly
INS Insurance Schedule Annual
+ 15 more types configured | 27 total
03

Real-time covenant health

The covenant monitoring engine uses a three-state calculation model. A simple compliant/breached binary is not enough for proactive risk management. The three states are:

Compliant. The metric is within the threshold with adequate headroom. No action needed, no risk flags.

AtRisk. The metric is still within the threshold, but headroom has fallen below the configured warning percentage. The borrower is technically compliant but trending toward trouble. This is where intervention is most effective.

NonCompliant. The metric has crossed the threshold. A breach has occurred. Immediate attention required.

Each covenant assignment stores the threshold value, the comparison operator (>=, <=, >, <), and a headroom percentage. The operator matters: a DSCR covenant requires the value to be greater than or equal to the threshold (e.g., DSCR >= 1.20x). An LTV covenant requires the value to be less than or equal to the threshold (e.g., LTV <= 75%). Getting the operator wrong would invert the compliance logic entirely.

Headroom is calculated as the percentage distance between the current value and the threshold. A DSCR of 1.35x against a 1.25x threshold has 8% headroom. If the headroom warning is set to 10%, this borrower would be flagged as AtRisk even though they are technically above the threshold. That early warning gives the fund manager weeks or months to engage with the borrower before an actual breach occurs.

covenant_monitor.dash
Borrower: Kivu Energy Ltd
DSCR AT RISK
Current: 1.35x Threshold: >= 1.25x
Headroom: 8% (warning at 10%)
ICR COMPLIANT
Current: 2.10x Threshold: >= 1.50x
Headroom: 40%
LTV BREACH
Current: 78.4% Threshold: <= 75%
Breached by 3.4 percentage points
04

7-tier automated escalation

Before CapitalBridge, the compliance team spent 15 to 20 hours per week composing and sending reminder emails to borrowers. Each email was written individually. Follow-up threads got buried in inboxes. When the person responsible for reminders was on leave, the process stopped entirely.

The escalation engine replaces all of this with a configurable 7-tier notification system. Each tier has its own timing (relative to the submission due date), its own email template, and its own recipient list. The first tier is a gentle reminder sent 7 days before the due date. The final tier is a management escalation sent 30 days after the deadline, copying senior stakeholders on both sides.

A Hangfire background job runs daily, evaluates every pending submission against the escalation schedule, and sends the appropriate notification for the current tier. If a borrower submits before the due date, no further reminders are sent. If they submit after tier 3 but before tier 4, the remaining tiers are cancelled. The system tracks which tiers have been sent and when, creating a complete audit trail of the reminder process.

The recipient list expands as escalation progresses. Early tiers go to the borrower's primary contact. Mid-tier reminders copy the borrower's CFO or compliance officer. Late-tier escalations include the fund manager and the relationship manager on the lender side. Each tier is configurable per fund, so different funds can have different escalation cadences based on their borrower relationships.

escalation_timeline.log
QMA Submission: Kivu Energy Ltd
T1 Sent
-7 days (Mar 24) : Pre-due reminder
T2 Sent
Due date (Mar 31) : Due date notice
T3 Sent
+1 day (Apr 01) : First overdue
T4 Sent
+3 days (Apr 03) : Second overdue
T5 Pending
+7 days (Apr 07) : Escalation to CFO
T6 Scheduled
+14 days (Apr 14) : Fund manager copied
T7 Scheduled
+30 days (Apr 30) : Management alert
Stack

The technology behind it

architecture.yml
Frontend
Razor Pages Blazor Components Metronic 7 Telerik UI (Kendo) Bootstrap 5
Application
ASP.NET Boilerplate (ABP) .NET 10 EF Core ASPNETZERO
Background Jobs
Hangfire Submission Scheduling Escalation Emails FX Rate Sync
Infrastructure
Azure Hosted SQL Server Azure Blob Storage Key Vault 256-bit TLS MFA

We chose ASP.NET Boilerplate (ABP) for its built-in multi-tenancy, audit logging, and permission system. In a fund management context, these are not nice-to-haves. Multi-tenancy means each fund operates with proper data isolation. Audit logging means every change to every record is tracked with the user, timestamp, and previous value. The permission system provides granular role-based access control: Fund Admins see everything across their funds, Analysts see their assigned portfolios, and Borrowers see only their own data.

Blazor handles the form submission data entry. Financial forms with 50+ fields, conditional validation rules, and currency-aware input masks are complex to build in traditional Razor Pages. Blazor's component model lets us compose form sections, validate on the fly, and provide a responsive editing experience without full page reloads. The rest of the application (dashboards, grids, document management, settings) is standard Razor Pages with Telerik Kendo UI components.

Hangfire runs three categories of daily jobs. First, the submission scheduling job that looks 90 days ahead and creates pending submissions based on active assignments. Second, the escalation engine that evaluates every pending or overdue submission against the 7-tier notification schedule. Third, the FX sync job that pulls spot and forward rates for all 156 currencies. These jobs run on their own schedule, are retry-safe, and produce detailed logs that the operations team can monitor through the Hangfire dashboard.

Production

Not a prototype

0 Funds in production
0 Borrowers
0 Countries
0 Weeks to go live
27
Reporting requirement types configured and active
156
Currencies with daily spot and forward rate sync
7
Escalation tiers per submission, fully automated

This is not a prototype, a demo environment, or a proof of concept. CapitalBridge runs real fund operations for Cygnum Capital every day. Borrowers log in to their portal to check deadlines and upload documents. The escalation engine sends reminder emails automatically. Covenant thresholds are evaluated on every form submission. Currency rates update every morning before the team starts work.

The implementation timeline was 4 weeks from kickoff to live submissions. Week 1 covered fund structure, sector hierarchies, and reporting requirement configuration. Week 2 was borrower data import and covenant threshold setup. Week 3 was borrower portal deployment and user onboarding. Week 4 was the first live submission cycle. The system has been running in production since then, processing submissions, monitoring covenants, and sending automated reminders every day.

Proceptio

What this means for you

We built CapitalBridge because we saw a gap in the market. Development finance institutions and private debt fund managers were running critical compliance workflows on spreadsheets, and the available software either targeted the wrong market (hedge funds, private equity) or was too generic to handle the specific requirements of DFI lending: cascading assignment rules, multi-currency covenant calculations, borrower self-service portals, and multi-tier escalation workflows.

But the engineering capabilities CapitalBridge required are not unique to fund compliance. The cascading rules engine is a pattern that applies to any hierarchical policy system. The multi-currency calculation layer works for any cross-border financial application. The automated escalation engine works for any workflow where deadlines matter and follow-ups need to happen without human intervention. The counterparty portal pattern works for any multi-party data collection process.

Proceptio is a software development team. We build data infrastructure for institutional clients. Our portfolio includes work for IKEA and Baillie Gifford. CapitalBridge is one product we have built from the ground up, and it demonstrates what we are capable of: taking a complex operational workflow, understanding the domain deeply, designing the data model and rule systems correctly, and delivering production-grade software that runs reliably every day.

If your organization has operational workflows running on spreadsheets, legacy systems, or internal tools that need replacing, this is the kind of thing we build. Complex data models. Rule engines. Multi-party portals. Automated workflows. Enterprise security. If the problem involves structured data, business rules, and multiple stakeholders, we have almost certainly solved something similar.