Academy · Solutions · Agentic Workflow
Build: Loan underwriting
In one line. A borrower’s application package — one fat PDF bundling the application form, financial statements, tax returns, bank statements, a collateral valuation — gets classified document by document, extracted per type, validated within and across documents, screened against sanctions and fraud, and the clean files sail straight through while the exceptions queue for an underwriter. You’ll build. A multi-document Processing pipeline: a Loan Underwriting collection, one classify-then-extract agent (upgradeable to a five-member Mesh), validation rules plus a sanctions/fraud guard rail, a 0.99-confidence straight-through-processing gate, an output-package export, and an underwriting dashboard. You’ll use. Collections & schema (D1) · Lifecycle, tags & events (D3) · Ingestion (D4) · Your first agent (A3) · Teams & mesh (A11) · Guard rails (A8) · Dashboards & views (E7)
This is B3’s invoice loop scaled up: instead of decide on each document, you decide on each application package made of many documents. A classifier works out what each document is, an extractor pulls the fields that type owns, rules reconcile the numbers across documents, and the lifecycle auto-advances anything clean and confident. Budget about 75 minutes for the single-agent happy path; two hours with the Mesh upgrade and the dashboard.
Watch out — build this in a fresh project. Underwriting taxonomies are big: one classification field plus dozens of extraction fields. On a production loan-origination build, adding ~12 labels under a new Learner in an already-busy project surfaced only 3 of them — label names collided with Labels under other Learners. In a fresh project the same bulk-add surfaces reliably. Create a clean project named
Loan Underwritingbefore Stage 1. If a Label ever fails to appear in the tree after Add, you are in a busy project — move.
Steps
Stage 1 — Collection and schema
Create the collection. In your fresh project: Studio ▸ Data Model ▸ Collections → + Collection (rail footer).
- Purpose: Processing (the default). Application packages move through a decision.
- Collection Name:
Loan Underwriting - Description:
Borrower application packages for classification, extraction, validation and credit decision - Leave every other accordion at its default. Click Save.
Flag-gated. The explicit Processing/Knowledge switch sits behind the
knowledgeCollectionsflag — if you don’t see a switch, new collections are Processing anyway. Purpose is immutable after create, so pick deliberately.
Checkpoint. Loan Underwriting appears in the left rail with the tab strip General · Schema · Lifecycle · Tags · Events · Ingestion · Agents. The Lifecycle tab is already seeded with the 8-stage Four-Eyes flow — you tune it in Stage 6.
Create the Learner. Open the Schema tab → footer Taxonomy:
- Taxonomy Name:
Application Package - Description:
Document type + loan-level and per-document fields - Type:
DocumentClassification(whole-document classification + field extraction) - Entity:
Loan Underwriting(pre-filled). Click Submit.
Add the classification field first. Select Application Package, then + Label. This field holds what kind of document this is:
| Label Name | Description | Type |
|---|---|---|
doc_type |
Which document this is: application form, financial statement, tax return, bank statement, collateral valuation, KYC document |
Plain text — the classifier writes one of these values into it |
Add the loan-level and per-document fields. + Label for each (Add between, Add and exit on the last). A field’s type comes from a validation rule under View Config plus Record Config — there is no single “type” dropdown (D1):
| Label Name | Description | Type |
|---|---|---|
borrower |
Borrower's legal name |
Plain text |
loan_amount |
Requested loan amount |
View Config → numeric rule; tick Enable Total Field |
dscr |
Debt-service coverage ratio (computed) |
View Config → numeric |
ltv |
Loan-to-value ratio (computed) |
View Config → numeric |
revenue |
Total revenue from the financial statement |
View Config → numeric |
net_income |
Net income from the tax return |
View Config → numeric |
stmt_balance |
Ending balance on the bank statement |
View Config → numeric |
collateral_value |
Appraised value of the pledged collateral |
View Config → numeric |
Every validation option is catalogued in the Schema field reference (R3).
Under the hood. A production underwriting taxonomy easily carries ~30 Labels across classification, document-intelligence and data-transform groups. You are building a teaching subset; the shape — one classification field, many extraction fields, confidence on each — is identical. Add the rest under Make it production-worthy.
Roadmap, not a click-path. Taxonomy governance — field-dictionary versioning, a Reviewer-to-Owner approval workflow, sensitivity tiers — is designed but not a shipped configurator surface yet. Don’t promise it.
Stage 2 — Ingestion
Clicks. Studio ▸ Connector ▸ Ingestion.
Flag-gated. This teaches the modern Sources v3 screen (the
ingestionV3lab flag). If you land on the older single-source wizard, enableingestionV3from the Lab menu (E1). Full detail: Ingestion & connectors (D4).
The fast path (one-shot, for testing now). Click Upload (toolbar) → drag in a sample application-package PDF → set Target collection = Loan Underwriting → Submit.
The recurring path (for production). A connector must exist before a job can use it:
- + Connector → pick Botminds Drive (this project’s internal drive — no credentials). Name it → Test connection → Save connector.
- + Job → Step 1: select that Drive connector. Step 2: Job name
Application packages intake; Browse… to the packages folder; Extensionspdf; tick Recursive; Target collectionLoan Underwriting. Step 3: Recurring → every 1 Hours (check the Next 5 runs preview) → Create job.
Under the hood — preprocessing. An application package is usually many documents in one PDF. The platform splits the package into per-document units (each gets a UUID and content hash) and deskews pages before the classifier sees them. You don’t configure this per job. A package arriving as a folder of separate PDFs works too — each file is its own unit.
Checkpoint. The Jobs tab shows
Application packages intake … active. Click Run now, then the Runs tab — the funnel counters (Enum → Fetch → New → … → Disp) fill, and clicking the run opens Run detail with a per-file log.
Stage 3 — The classifier + extractor agent
One agent that, per document, decides its type and fills the fields that type owns. Start with a single structured-output agent; Stage 4 shows when to split it.
Clicks. Studio ▸ Agent Builder ▸ Agents → footer + Agent ▸ Agent (A3).
- Persona: Name
Underwriting Document Processor; DescriptionClassifies each application document and extracts its fields. Use the co-pilot on Instructions, seed it with “Classify a loan-application document and extract its fields,” then refine to roughly:You process documents from a borrower’s loan application package. First decide the document type — one of: application form, financial statement, tax return, bank statement, collateral valuation, KYC document — and write it to
doc_type. Then extract only the fields that document supports (e.g. revenue from a financial statement, net income from a tax return, ending balance from a bank statement, appraised value from a collateral valuation). Never invent a value the document doesn’t show; leave it blank instead. - Model: pick your project’s LLM Model. Set Agent Mode = Thinking while you tune.
- Output: turn on Structured output. Pick the Taxonomy/Learner
Application Package, set Process unit = Page, confirm each Label carries a short Description, and turn on Confidence Score and References. Per-field confidence is what routes a package to an underwriter later. - Click Save Agent — there is no autosave.
Test in the Playground before wiring it up. In the right pane: Input Type = Document, pick a sample application document, press Enter. Watch doc_type resolve and the right fields stream in with confidence and page references. Flip Agent Mode = Fast and re-run — same result, quicker; that is your production setting.
Assign it as the collection’s Worker. Studio ▸ Data Model ▸ Collections ▸ Loan Underwriting → Agents tab → Assign Underwriting Document Processor as the intake/processing agent. Every document that lands is now run by it automatically (a Worker is not a Reader — D1).
Honesty callout — live LLM agents on a local stack. On a production build we hit real platform bugs running a live agent end to end: the bound model didn’t always stick at run time, the fallback model wasn’t deployed on the local key, and a document-attached run could throw inside the document runner. If your Playground works but the assigned Worker stalls at 0% on a real document, confirm the agent is bound to a deployed model — one your key actually serves — not a default that maps to an undeployed name. See LLMs & services (A2).
Stage 4 — Optional upgrade: the five-member Mesh
Decompose the single agent into a durable, multi-stage pipeline so each stage is tuned and tested on its own, survives a restart, and can fan out parallel checks. Optional — the Stage-3 agent already works; reach for a Mesh only when the automation is long, branching, must survive restarts, or has steps over 240 seconds (OCR on a 200-page valuation report, a slow partner screening API). Full decision guide: Teams & mesh (A11).
Clicks. Studio ▸ Agent Builder ▸ Agents → + Agent ▸ Mesh (always available — not flag-gated). This opens the XFlow designer in mesh mode; the palette is XFlow Operator + Queue + Service.
The shape to draw — five members wired by queues:
package ─► classify ─► q.a ─► validate ──┬─► enrich ───────┐
(XFlow) (XFlow) │ (XFlow) ├─► approve ─► summarize
└─► screen ───────┘ (agent) (sink)
(sanctions/fraud
Service, parks)
- classify, validate, enrich are XFlow Operator nodes, each referencing a runnable.
- screen is a Service node — it parks (
awaiting) while an external screening worker does the slow check and reports back. This is the platform’s answer to the 240-second member ceiling (A2). - approve is an agent member that writes the decision; summarize is the sink that writes the Summary / output package.
- A Queue box sits between members; node→queue publishes the baton, queue→node subscribes. Two members on one queue = fan-out (validate feeds enrich and screen in parallel); one member with two incoming queues = fan-in (approve waits for both).
Click Save Mesh. MeshId is global, not per-project — pick a unique id like loan-underwriting-uw. Watch a run on the Runs tab: a member timeline with per-member states (done / running / awaiting service), a queue-health banner, and a Baton trail of pub/sub hops. The awaiting service dot on the screen step means it is parked on your worker — not stuck.
Assign the Mesh as the collection’s Worker exactly as in Stage 3 — intake routes by kind (mesh → agent → xflow).
Under the hood — the run ledger. Each mesh run is recorded as a durable trace: per-segment timestamps, baton hops, member states. On a production lending build this was the immutable audit fabric. You don’t build the ledger; running the mesh produces it.
Tip. Don’t reach for a Mesh too early. If the single agent processes a package correctly and you don’t need durability, parallel screening, or a >240 s step, ship the single agent.
Stage 5 — Validation and the sanctions/fraud guard rail
Within- and cross-document validation.
- Within-doc: the numeric rules on
loan_amount,dscr,ltv(Stage 1’s View Config) reject garbage — negative, non-numeric. Set on the Label, enforced on every document. - Cross-doc: the reconciliation checks — does
net_incomefrom the tax return agree with the financial statement’s story? doescollateral_valuesupport theltvagainstloan_amount? doesdscrhold givennet_incomeand the requestedloan_amount? Express these as a Conditional automation on the Validate stage of the lifecycle, or as the validate member’s own logic in the Mesh. Detail: D3.
The sanctions/fraud screen as a guard rail. A guard rail is a named policy object attached to the agent (A8). Studio ▸ Agent Builder ▸ Guard Rails → + Guard Rail:
- Name:
Sanctions & fraud screen - Description:
Flag any borrower name on a sanctions/PEP watchlist and flag anomaly patterns - Instruction: “If the borrower’s name matches a sanctions or PEP watchlist, or the document shows tampering / inconsistent fonts / altered numbers, set a fraud flag and never auto-approve — route to human review.”
- Save. Then attach it: edit
Underwriting Document Processor(or the Mesh’s approve agent) → Governance tab → Guard rails ▸ + Add → pickSanctions & fraud screen→ Save Agent.
Honesty callout — a real sanctions screen is an external service. A genuine watchlist check calls a live screening API and can be slow — exactly why Stage 4 modelled it as a Service that parks, not inline agent reasoning. The guard rail sets the policy (flag, never auto-approve); the lookup belongs in a Service or Tool against your real list. The LLM does not “know” the watchlist.
Checkpoint. The agent’s Governance tab shows the
Sanctions & fraud screenshield chip; validation rules show on the relevant Labels’ View Config.
Stage 6 — The lifecycle gate: STP by default, HITL by exception
Clicks. Studio ▸ Data Model ▸ Collections ▸ Loan Underwriting → Lifecycle tab. The 8-stage Four-Eyes flow is already seeded; you add one Auto-Decide rule and confirm routing (D3).
- Below the stage graph, open the Auto-Decide policy card:
- Enabled = true
- Applies To = approve — auto-advance only; keep human eyes on declines.
- Min Confidence = 0.99 — the production straight-through gate.
- Require Zero Flags = true — any sanctions/fraud flag from Stage 5 forces a human.
- Optional: a Max Amount so only loans under a threshold auto-advance. Note the evaluator compares the agent’s self-reported
_meta.amount, not a schema field by name — the cap only fires if the Worker surfaces the loan amount in its_meta. - Save. The preview reads roughly “Auto-advance when AI confidence ≥ 0.99 AND zero flags.”
- Click the L1 Review stage → confirm Send to Inbox (human review) is on (
IncludeManualIntervention); same for L2 Approval. Seeded on — you are verifying, not adding.
Watch out. Don’t use Reset to default on a lifecycle with live documents — it is destructive.
Under the hood — the audit trail is the decision. There is no separate “decision” object. The stage-move history — who advanced it, at what confidence, with what flags — is the record; an automatic advance is stamped by the auto-decide actor, a human one by name (G5). On the Mesh path, the run ledger from Stage 4 is the inference-level companion.
Stage 7 — Review, decide, export
What an underwriter does. A package that doesn’t auto-advance enters L1 Review and an Inbox item wraps it:
- Consumer app ▸ Inbox (route
inbox). Each card shows the borrower, a provenance line (wraps ▸ Loan Underwriting · doc-… · L1 Review), age, and why it is here — low confidence, missing document, sanctions flag. The Inbox doesn’t act — read, snooze, dismiss only (E5). - Click Open → Document Detail (
/<project>/:docId) — the document on one side, extracted fields on the other (E3). - Read the flagged low-confidence fields first. Click a value — say DSCR : 1.08 — and the viewer scrolls to and highlights the source segment it was read from. That is how you catch a mis-read or a cross-doc mismatch.
- Correct a wrong value in place (the edit becomes a teaching signal — A9); Rescore if
dscr/ltvneed recomputing. - Advance from the workflow action pane: approve → L2 Approval (second eye) → Approved. Every move writes the audit trail.
Export the output package. Studio ▸ Connector ▸ Output → + Export (D4):
- Name:
Underwriting output package - Export Type: a structured (split/bookmark) type, so the package ships as data plus provenance. A production build delivers a canonical JSON output package to the bank’s loan-origination API.
- Destination: Azure Blob, Botminds Drive, or a Webhook to your loan-origination endpoint.
- Toggle Export input document to ship the original package alongside the data. Save.
Fire it on decision. Loan Underwriting ▸ Events tab → + Event → trigger stage change, Workflow Four-Eyes, Stage Approved, + Add Action → Webhook to your endpoint (D3).
Stage 8 — The underwriting dashboard
Clicks. Studio ▸ Intelligence → select or create a dashboard → add widgets (E7). Building a widget is: add → pick template → optional chart type → point at data → save. Four tiles:
- Packages — a Documents Count widget (total ingested this period).
- STP rate — a ratio: auto-advanced (auto-decide actor in the audit trail) ÷ total.
- Pending — a Documents Count filtered to L1 Review + L2 Approval.
- Exceptions / watchlist hits — a count filtered to documents carrying a review flag or the
Watchlist Hit/Fraud FlagTag (add these on the Tags tab first).
Tip. Not sure which template fits? Use the Agentic Widget — describe the tile in plain language (“STP rate this month”) and let AI build it.
Checkpoint. Open live dashboard jumps to
/_dashboard?did=<id>— the exact view your credit-ops users get. The Dashboard tab is hidden from theclientrole and under theDisableDashBoardflag.
Stage 9 — Test it end to end
About fifteen minutes:
- Upload.
Studio ▸ Connector ▸ Ingestion→ Upload → one clean application-package PDF and one messy one → Target collection = Loan Underwriting → Submit. The result panel reports “N of M registered.” - Watch it process. The document list shows a Processing… chip climbing to 100% as the Worker classifies each document and fills the fields. Stuck at 0%? The background worker isn’t running, or the agent is bound to an undeployed model — see the Stage 3 callout.
- See classification + extraction. Open a document;
doc_typeresolved per document, the right per-type fields filled with confidence and page references. - See routing. The clean package auto-advances (stamped by the auto-decide actor); the messy or flagged one lands in the Inbox under Needs Review.
- Review it. Open the Inbox item → Open → verify a flagged field against its highlighted source, correct one value, Rescore if DSCR/LTV shift, approve through L2 Approval → Approved. The Inbox item disappears.
- See it exported. The Approved package triggered your
Underwriting output packageexport. - Read the dashboard. Packages ticks up, STP rate reflects the auto-advanced file, Pending and Exceptions reflect the rest.
All seven and you have a working, audited loan-underwriting pipeline — built by configuration.
Make it production-worthy
- More document types and fields. Add the remaining per-doc fields (credit score, guarantor details, employment- or business-verification dates) as Labels on
Application Packageand matching Labels on the agent’s Output tab, then Rescore. Schema and agent must agree or the field stays empty. - Exception tags and a reviewer View. On the Tags tab add
Watchlist Hit,Fraud Flag,Missing Document,Income Mismatch; then define an Exceptions View (columns: Borrower, Loan Amount, DSCR, Flag, Status), Type = Project, Allowed roles = Reviewer (E7). - Cost telemetry. A production build tracks cost per application / document / stage / field with a spike alert. Surface compute-cost analytics on the dashboard.
- A second model. Build a second extraction agent on a different LLM and swap it in — the agent’s Model tab is a single swap point, which is the proof against model lock-in (A2).
- Graduate to the Mesh. When you need the sanctions screen parked as a Service or credit and fraud checks fanned out in parallel, move from the Stage-3 agent to the Stage-4 Mesh.
Where to go next
- The same loop in its smallest form: B3 · Invoice settlement.
- Mesh, teams, and when each earns its keep: Teams & mesh (A11).
- Scale from one workflow to a governed domain: B9 · Capstone: Lending domain brain.
Prefer learning inside the product? The same academy lives in the platform's Learn menu — every screen links to the chapter that explains it.
See the platform live