Work / 02

Holdback processing

Automating a manual reconciliation in regulated finance

Replacing a spreadsheet-and-eyeballs process for mortgage holdback disbursements with a service that shrugs off monthly format changes.

My role
Full-stack engineer (contract). I owned the holdback workflow and tables, file ingestion, the CRUD API, the enrichment and validation endpoint, and the Temporal worker integration. Wire matching and message generation belonged to another engineer; a teammate set architecture direction.
Context
The insurance group of a global alternative asset manager. Ops, a PM and an offshore team.
When
Jun to Sep 2026
Stack
  • Python
  • FastAPI
  • SQL Server
  • Temporal
  • Snowflake
  • Cosmos DB
  • Flux CD
  • Azure
  1. 01Servicer file landsFormat rules load from config, not code.(built by Jimmy)
  2. 02IngestionParse, normalize, write to the holdback tables.(built by Jimmy)
  3. 03Validate + enrichOne CTE against the position table. One round trip.(built by Jimmy)
  4. 04Temporal workflowSlow enrichment runs fire-and-forget, off the intake path.(built by Jimmy)
  5. 05Wire matching + messagesDownstream disbursement steps.(built by others or planned)
MineOthers or plannedOne servicer file's path through the service. Filled steps are mine.

Covered by an NDA. Described at the architecture level, with no internal names, figures or screens.

The problem

Operations staff reconciled mortgage holdback disbursements by hand. Two things made that hard to automate. Servicer files changed format from month to month. And the data needed to validate each loan lived in a large, unindexed position table.

Details here are kept at the architecture level. No internal names, figures or screens.

Decisions

1. One query, not two round trips

Validation and accounting enrichment both needed the same position data. I folded them into a single CTE, so each file costs one database round trip instead of two.

Tradeoff: a denser query to read and review, in exchange for per-file cost that stays flat as volume grows.

2. Keep slow work off the intake path

Enrichment for high-volume files was slow enough to block intake. I moved it behind a fire-and-forget call from the Temporal workflow, so heavy enrichment never holds up the next file.

Tradeoff: eventual consistency on enrichment, which the workflow already tracks and retries.

3. Rules that change monthly belong in config

Servicer formats change every month, and that shouldn’t require a release. Extraction rules live as Cosmos DB config, so a format change is a data change.

Tradeoff: config needs its own validation and review, which I wrote into the runbook.

Working in it

Before writing code I ran functional and technical refinement with ops, the PM and the offshore team to pin down validation rules. I also set up delivery for the new repo: Temporal workers, Flux CD deploys to Azure Container Registry, and Snowflake schema migrations. Then I scaffolded the UI and scoped its contract with the API.

The honest ownership line matters to me: I built the intake, validation and workflow side end to end. I didn’t build the whole disbursement system.