Immigration Financial Audits in 60 seconds with LandingAI + Bedrock


TL;DR
Canadian immigration officers manually review 6 months of bank statements per applicant. That process takes 2 to 4 hours and must apply different rules across 10 distinct IRCC programs. Openomi compresses it to about 60 seconds by chaining LandingAI Agentic Document Extraction for structured document extraction with an AWS Bedrock Agent running Claude 3.5 Sonnet for compliance reasoning and fraud detection. Before each audit begins, the agent queries a RAG knowledge base for the exact fund thresholds and statement history requirements tied to the applicant's specific program, so FSW-EE rules apply to FSW-EE applications and Quebec rules apply to Quebec applications, never crossed. IRCC processed more than 7 million decisions in 2024 (IRCC Departmental Results Report, 2025) while investigating about 9,000 suspected fraud cases per month, and document fraud, particularly in bank statements, is rising.
The Daemon Craft team built and shipped the full working system, which has since grown into a production SaaS platform at openomi.io, now live and serving Regulated Canadian Immigration Consultants (RCICs) across Canada and immigration candidates worldwide. You can explore the source code on GitHub and see the core extraction workflow run end to end in the demo video.
The Problem: Manual Financial Audits Don't Scale Across Programs
IRCC processed more than 7 million decisions across all immigration streams in 2024, while investigating approximately 9,000 suspected fraud cases per month (IRCC Visa Integrity briefing, March 2025). Visitor visa refusals for misrepresentation rose 251% in 2024 compared to 2023. The forgery problem is getting worse, not better: template-based document fraud rose 49.8% from 2023 to 2024, and bank statements account for 59% of all fraudulent documents detected by AI fraud systems (Inscribe AI 2025 Document Fraud Report).
For any automated system, the hard part is not volume. It is staying consistent across programs that follow very different rules:
- Federal Skilled Worker (FSW-EE): Minimum CAD $15,263 for a single applicant, 6 months of consecutive statements required, any single deposit over $5,000 within 60 days without documentation triggers rejection
- Canadian Experience Class (CEC-EE): No proof of funds required at all, so flagging a CEC applicant for missing bank statements is a false rejection
- Quebec Skilled Worker (QSW-ARRIMA): Only CAD $3,462 minimum for a single applicant, only 3 months of statements required, documents must be in French or accompanied by translation
- Family Sponsorship: No proof of funds for the applicant. It is the sponsor's income that gets audited, under a completely different framework
Mixing these rulesets produces incorrect verdicts. An automated system must know which ruleset applies before it checks anything.

Architecture: Three Layers, One Workflow
This walkthrough covers the open-source build in the GitHub repository, the foundation the production platform grew from. That build splits the work into three concerns:
- Document intelligence: LandingAI ADE extracts structured JSON from uploaded PDFs and images
- Compliance reasoning: AWS Bedrock Agent (Claude 3.5 Sonnet) queries program-specific rules from a RAG knowledge base, then applies fraud detection logic across all extracted documents
- Orchestration and UI: Streamlit frontend handles program selection, file upload, and report rendering; Amazon S3 stores documents between the UI and the Lambda extraction tool

LandingAI Agentic Document Extraction does not do the reasoning; it handles extraction. The Bedrock Agent does not parse documents; it calls the Lambda tool that wraps Agentic Document Extraction. Each layer has one job.
Document Extraction with Agentic Document Extraction
The extraction pipeline in src/openomi_logic.py runs two steps for every uploaded file:
# Step 1: Parse document to markdown\
parse_response = ade_client.parse(
document_url=str(local_file_path),
model="dpt-2-latest"
)
# Step 2: Extract structured JSON from markdown
json_data = ade_client.extract(
schema=SCHEMA_JSON,
markdown=parse_response.markdown,
model="extract-latest"
)The parse step (dpt-2-latest) converts a PDF or image to markdown, handling multi-column layouts, handwritten annotations, bank stamps, and non-standard formatting. The extract step (extract-latest) maps the markdown to a typed Pydantic schema:
class Transaction(BaseModel):
date: str
description: str
amount: float # negative for withdrawals\
class BankStatementSchema(BaseModel):
account_holder: str
open_balance: float
ending_balance: float
currency: str
transactions: list[Transaction]The schema is the contract between ADE and the Bedrock Agent. The agent does not receive raw text. It gets a clean, typed JSON object it can reason over directly.
This separation matters for real documents. Bank statements vary a lot across institutions. Some use three-column formats, others embed transaction tables in image layers, and some include branch stamps that overlap text. The parse model (dpt-2-latest) is built for layout understanding. The extract model (extract-latest) is built for schema-aligned field extraction. Separating the steps lets each model focus on what it does best.

(Rendered from the inline Markdoc SVG chart in the source file; exported as a static PNG for this package.)
Wiring LandingAI ADE as a Bedrock Agent Action Group
The Lambda function in src/openomi_logic.py connects the Bedrock Agent to LandingAI ADE. AWS Bedrock's Action Group pattern lets you expose any callable as a tool the agent can invoke mid-reasoning. The connection is declared via an OpenAPI schema (openapi_schema.json) rather than custom routing code, so the agent understands the tool's interface declaratively.
The SAM deployment in template.yaml configures the Lambda:
OpenomiExtractionToolFunction:
Type: AWS::Serverless::Function
Properties:
Handler: openomi_logic.lambda_handler
Runtime: python3.11
MemorySize: 2048
Timeout: 600
Layers:
- !Ref OpenomiDependenciesLayer
Environment:
Variables:
S3_UPLOADS_BUCKET: !Ref UploadBucketName
VISION_AGENT_API_KEY: !Ref LandingAIApiKeyThe 2048MB memory allocation and 600-second timeout reflect real document processing requirements. Multi-page bank statement PDFs are large, and ADE's parse step handles layout complexity that should not be rushed. The Lambda Layer bundles landingai-ade, pydantic, and boto3 separately from the function code, keeping deployment packages small.
When the Bedrock Agent decides to extract a document, it calls /extract_document with a file_key, the S3 object key for a previously uploaded file. The Lambda downloads the file, runs the two-step ADE pipeline, and returns structured JSON. The agent repeats this call for every statement in the batch, then aggregates balances and transaction histories before starting compliance analysis.
Program-Specific Compliance via RAG
The most important design decision in Openomi is not document extraction. It is keeping compliance rules out of the agent's system prompt.
The agent's instructions tell it to query the RAG knowledge base for three specific things at the start of every audit:
Query 1: "What are the financial requirements for [Program Code] with family size [X]?"
Query 2: "What are the high-priority red flags for [Program Code]?"
Query 3: "What are acceptable sources of funds for [Program Code]?"The knowledge base (ircc-all-programs-financial-requirements.md) holds the actual IRCC program rules. When IRCC updates fund thresholds, which happens by regulatory cycle rather than by product roadmap, the knowledge base document gets updated. Nothing else changes.
The anti-pattern is hardcoding thresholds in the system prompt. When "$15,263 for FSW-EE single applicant" is baked into the agent's instructions, a regulatory change requires a code change and a redeployment. In a RAG-backed setup, it is a document update. For any compliance system where rules are externally governed, this separation is not optional.
The fund thresholds vary widely across programs, which is why the RAG lookup runs before any document is analyzed:
| Program | Minimum Funds (Single Applicant) | Statement History |
| FSW-EE | CAD $15,263 | 6 months consecutive |
| FST-EE | CAD $15,263 | 6 months consecutive |
| QSW-ARRIMA | CAD $3,462 | 3 months |
| CEC-EE | None required | N/A |
| Family Sponsorship | None (sponsor audited) | N/A |
| PNP | Province-specific | Varies |
Source: IRCC program requirements, 2024-2025.
What Openomi Produces
The Streamlit frontend runs a three-phase workflow: program and family size selection, bank statement upload (PDFs and images), and AI analysis via Bedrock Agent. The upload phase pushes files directly to Amazon S3 via boto3. The analysis phase invokes the Bedrock Agent with a prompt that includes the program code, family size, and the S3 keys of every uploaded document.
The agent generates a structured markdown audit report with five sections:
- Executive Summary: APPROVED / NEEDS REVIEW / REJECTED verdict, risk level, and a one-line decision rationale for the reviewing officer
- Financial Overview: total funds available, compliance status against the program's minimum, statement history length check, and per-statement balance summary
- Red Flags Detected: tiered HIGH / MEDIUM / LOW flags, each with the specific evidence that triggered it (for example, a single deposit over $5,000 within 60 days, or income inconsistencies across statements)
- Compliance Check: tabular pass/fail against every program-specific requirement
- Fraud Analysis: document authenticity assessment, suspicious deposit pattern detection, borrowed funds indicators, and AI confidence score
The UI parses the verdict from the agent's response and renders a color-coded banner. Red flag count is extracted from the report text. Results export as JSON or plain text for downstream use.
From Demo to Production Platform
The system described above is the build you can run from the GitHub repository. Since then, the Daemon Craft team has grown Openomi into a full production SaaS platform at openomi.io, now live and serving Regulated Canadian Immigration Consultants (RCICs) across Canada and immigration candidates worldwide.
The production platform extends well beyond the original Streamlit demo:
- AI document audits: the bank statement audit described here, now one of five distinct document audit types
- CRM with Kanban pipeline: case and client management for immigration consultants
- Test preparation: TCF Canada and IELTS prep with AI grading
- IRCC form filling: automated completion of IRCC forms
- Bilingual expert chat: immigration guidance in English and French
- Built-in IRCC phone calls: in-platform calling
The AI stack scaled with it. Openomi now uses Google Gemini as a reasoning and grounding model alongside AWS Bedrock, Claude-powered services, and LangChain agents for specialized tasks and workflow orchestration. The architecture has expanded, but the LandingAI ADE extraction pipeline described in this article remains the core engine for processing immigration documents.
Three Lessons for Builders
1. Separate parsing from extraction.
LandingAI ADE's two-model flow, parse() with dpt-2-latest then extract() with extract-latest, maps onto two distinct problems. The parse model handles layout understanding: varied column formats, handwriting, stamps, image-embedded tables. The extract model handles semantic mapping: reading markdown content and populating typed fields from a Pydantic schema. Combining both into a single prompt asks one model to solve two very different problems at once. The split produces more reliable results on real documents.
2. Keep compliance rules in the knowledge base, not the system prompt.
IRCC program rules change by regulatory cycle. Any threshold, requirement, or exception that an external authority controls should live in a retrievable document, not hardcoded into agent instructions. The agent's system prompt defines how to audit; the knowledge base defines what to audit against. This pattern applies to any compliance domain where rules are externally governed and subject to change.
3. Lambda plus an OpenAPI schema is the cleanest pattern for Bedrock Agent tool calls.
Declaring the extraction tool through openapi_schema.json rather than custom routing code means the agent understands the tool's interface declaratively. Adding a second tool, say a currency conversion API, a sanctions list lookup, or a payslip extractor, is an OpenAPI schema addition and a new Lambda handler. The agent orchestration layer does not change.
Frequently Asked Questions
Can Openomi's extraction be adapted to other document types beyond bank statements?
Yes. The extraction schema is the only component tied to bank statements. Replacing BankStatementSchema with a PaystubSchema or TaxReturnSchema and updating the Pydantic model is the only required change in the Lambda function. The Bedrock Agent, RAG knowledge base, and Streamlit frontend all stay the same.
What are the current limitations of Openomi's verdict parsing?
The verdict (APPROVED, NEEDS REVIEW, or REJECTED) is parsed from the agent's response using string matching on the output text. When the agent's phrasing varies from expected patterns, this can misclassify. A better approach would be to have the agent return a structured JSON object with a dedicated verdict field alongside the markdown narrative, removing the dependency on text pattern matching entirely.
Why use AWS Bedrock Agent instead of calling Claude directly via the Anthropic API?
Bedrock Agent manages multi-step tool orchestration natively: it decides which tools to call, in what order, and how to aggregate results across multiple documents. Calling Claude directly via the Anthropic API would mean writing that orchestration loop manually: managing tool call and response cycles, tracking multi-document state, and routing between extraction and reasoning passes. For a workflow where the number of uploaded documents varies per applicant, the Bedrock Agent Action Group pattern is much simpler to build and maintain.