Pixeldev

Work/Investor onboarding for Bowery

A staged onboarding workspace that reads the application form, runs the compliance checklist and files the result into the client's own Microsoft 365 register.

Bowery runs two private credit funds and onboards wholesale investors by hand: a paper checklist, a shared Excel register and a SharePoint folder per investor. I built them an internal tool that takes a signed application from upload to sign-off, without moving their records out of Microsoft 365. Their staff still make every decision. The app does the reading, filing and bookkeeping around those decisions.

Client
Bowery
Industry
Private credit funds management
Services
Workflow design, full-stack build, Microsoft 365 and HubSpot integration
Stack
Next.js 16, tRPC, Prisma and Postgres, Microsoft Graph, Python extraction worker
Timeline
June to October 2026, 130 commits
Bowery pipeline board listing ten investor cases with fund, entity type, register entry, a per-stage progress bar, dates and a coloured status with a one-line reason
The pipeline. Every row says where the case is stuck and what to do next, so the board doubles as the day's to-do list.
26items on Bowery's AML/KYC checklist, each one tracked per case
833unit and extraction tests across the app and the PDF worker
31sample application forms the extractor is tested against

The problem

Bowery onboards investors into two funds with different rules: the Diversified Credit Fund and the Mortgage & Investment Fund. Each application arrives as a signed PDF, sometimes filled in on screen, sometimes printed, handwritten and scanned. Staff retyped it into an Excel Master Workbook, filed the paperwork into a SharePoint folder per investor and ticked through a 26-item paper checklist that changes with the investor type: an individual needs different checks from a trust with a corporate trustee.

The register is the firm's record of truth, so whatever I built could not replace it. It had to fit around it, and leave every compliance decision with a person. As their operations manager put it, every new investor meant an afternoon of retyping, and nobody could say for sure which checks had been done.

What I built

A single-tenant Next.js app behind Microsoft sign-in, locked to Bowery's own tenant. A case moves through seven stages: register link, application, verify, general requirements, entity and structure, AML/KYC, then review and completion. The checklist is Bowery's paper one turned into config, so each investor type only sees the items that apply to it, and the rest are folded away as not applicable rather than hidden.

On Verify, the original PDF sits beside every field the app read from it. Each field carries a confidence score from 0 to 100, anything under 60 is flagged, and nothing goes forward until a person has confirmed or corrected it. Once Verify is confirmed, checklist items the confirmed data already satisfies are pre-ticked with their source shown, and staff can untick any of them.

  1. 1Upload the signed application. It lands in a staging folder in Bowery's SharePoint, never on my servers.
  2. 2Link the case to a person in the register. The app suggests matches, but a human always makes the choice.
  3. 3Confirm the extracted fields against the PDF, then work through the checklist.
  4. 4Key in the investor number from Axcess. That writes the register row, and the HubSpot push is a separate, optional step.
  5. 5An independent reviewer clears the last items and signs off. Only then does the case lock.
Verify stage showing the original application PDF on the left and extracted fields on the right, each with a confidence percentage, one medium-confidence date of birth flagged for review
Verify. The form on one side, the app's reading of it on the other, with a confidence score on every field.

The hard part

Reading the forms reliably. Extraction runs in a Python worker built on pymupdf4llm, started fresh for each job in a Docker container with no network and a read-only filesystem, so an untrusted PDF never gets near anything that matters. The worker reads live form widgets, typed text, OCR of scanned pages and the ink inside tick boxes, and scores each field from the evidence it actually found. Those scores are tested against 31 sample forms: fillable, flattened, poorly scanned and rotated.

Writing to a spreadsheet that people also edit by hand. The Master Workbook stays the source of truth, so every write goes through Microsoft Graph. Columns are looked up by their header on every call, and a renamed or duplicated column stops the write and reports it, rather than writing into the wrong cell. Rows are keyed by a Case Reference column the app adds. Writes are serialised with a Postgres advisory lock. Folder creation is idempotent, and before creating a person's folder the app looks for an existing one, matching the naming conventions staff had used across 695 existing folders.

Completion is one ordered action. Saving the investor number writes the register row first. A failure leaves the case visibly partial with a retry button, and nothing reaches HubSpot until the register has the row. Personal details are encrypted per record with AES-256-GCM, files are streamed from SharePoint on the viewer's own token, and the audit trail is append-only and never records personal details.

Review and completion stage with the independent review checks above a numbered sequence: investor created in Axcess, investor number recorded, register row updated and synced, an optional HubSpot push, and sign-off waiting on the reviewer
Completion, shown in order. The register row is written when the number is saved. HubSpot is optional, and sign-off waits for the reviewer.

How it went

I built it over 130 commits between June and October 2026, working from Bowery's own checklist and register rather than a generic CRM template. Every external system has an in-memory fake behind a flag, so the whole flow runs locally with no Microsoft tenant. That is also how the 674 app tests cover the completion sequencing without Postgres or Graph.

Since it went into daily use, a typical case moves from received application to sign-off in two to three days instead of around two weeks, and nobody retypes an application into the register any more. The review screen was the part the team warmed to first: in their words, they can finally see what was checked, by whom and against what, and the register still looks exactly like theirs.