AI-powered agent for automating Initial Flood Determination (IFD) lookups on the FEMA Map Service Center.
The IFD Agent automates the manual Initial Flood Determination pipeline:
- Read the subject property address from the Encompass loan record.
- Drive
msc.fema.govwith Playwright, type the address into the FEMA search bar, and capture the rendered map page asFLOODSEARCH.pdf. - Render the captured PDF to PNG with PyMuPDF and ask a Bedrock vision call (forced
toolSpec) to extract the FEMA flood zone, SFHA status, FIRM panel number/effective date, community name, and community ID. - Write the extracted flood zone to Encompass standard field 1387 (Flood Zone).
- Upload
FLOODSEARCH.pdfto Encompass eFolder bucket 132 - Flood Search.
- FEMA Map Service Center automation — Playwright fills the address search bar and waits for the map + SFHA panel to render before capturing the PDF.
- AI-driven flood zone extraction — a Bedrock vision model with a strict JSON-schema
toolSpecparses the captured map; thezone_foundfield gates downstream writes. - Encompass integration — Writes field 1387 first (fail safely if the field write fails), then uploads
FLOODSEARCH.pdfto bucket 132. - Eligibility & expiration checks — Skips DFT/CANCELED/DENIED loans and any loan whose bucket 132 already has a document under 30 days old.
loan_id
-> connect to Encompass
-> eligibility check (DFT/CANCELED/DENIED, state, loan type)
-> 30-day expiration check on bucket "132 - Flood Search"
-> get_property_address (loan.property.{streetAddress,city,state,postalCode})
-> capture_fema_pdf_async(address) (Playwright -> FLOODSEARCH.pdf)
-> extract_flood_zone(pdf_bytes) (PyMuPDF -> Bedrock vision)
-> if zone_found != "yes": short-circuit with status=needs_review
-> update_custom_fields(field 1387 = <flood_zone>)
-> upload_file_into_efolder(portal="IFD")
ifd_agent/
├── ifd_agent.py # Main agent entry point (AgentCore + Strands)
├── encompass_mcp_tool.py # Strands tools (process_encompass_request, ...)
├── encompass_functions.py # Core single-property processing flow
├── playwright_assistant/
│ └── capturePDF_async.py # FEMA portal Playwright automation
├── vision_assistant/
│ └── flood_zone_extractor.py # PyMuPDF + Bedrock vision tool-use
├── encompass_assistant/
│ ├── exp_apis.py # Encompass API helpers
│ ├── get_property_address.py # Subject property address lookup
│ ├── get_connection.py # Encompass connection
│ └── upload_file.py # eFolder upload
├── utils/ # Shared helpers (misc, s3, loan-details debug)
├── models.py # Pydantic response models
├── process_tracker.py # Step-by-step process tracker
├── efolder_mapping.json # Portal -> bucket mapping (IFD -> 132)
├── requirements.txt # Agent-specific deps (pymupdf, pypdf, ...)
├── Dockerfile # Multi-stage Playwright + af-tools image
└── tests/ # Smoke tests
{ "prompt": "Process loan 87025103184 for IFD portal" }Or, equivalent shorthand:
{ "prompt": "87025103184" }{
"status": "success",
"loan_id": "87025103184",
"request_id": "abc-123",
"processes": [
{
"code": "ifd",
"execution_state_code": "completed",
"steps": [
{"code": "get-property-address", "execution_state_code": "completed"},
{"code": "search-website-fema", "execution_state_code": "completed"},
{"code": "capture-website-pdf-fema", "execution_state_code": "completed"},
{"code": "extract-flood-zone-vision", "execution_state_code": "completed"},
{"code": "write-flood-zone-field", "execution_state_code": "completed"},
{"code": "upload-to-encompass-efolder-132-flood", "execution_state_code": "completed"}
]
}
],
"response": "..."
}| Variable | Description | Default |
|---|---|---|
AWS_REGION |
AWS region | us-east-1 |
BEDROCK_MODEL_ID |
Bedrock model for the main Strands agent | (set per env) |
FLOOD_ZONE_EXTRACTOR_MODEL_ID |
Bedrock model used by the vision extractor | us.anthropic.claude-sonnet-4-6 |
PLATFORM_SECRETS_ARN |
AWS Secrets Manager ARN with Encompass credentials | (required) |
RUNTIME_NAME |
AgentCore runtime ID used for CloudWatch log group | ifd-agent |
- Single PDF capture path — direct async Playwright only, no MCP-playwright fallback.
- Vision is the only zone extractor — no DOM-scrape fast-path on FEMA's portal.
zone_found != "yes"-> short-circuit — returnstatus=needs_reviewwith the extracted dict and skip both the field write and the eFolder upload.- Field write before eFolder upload — if PATCH on field 1387 fails we don't dirty bucket 132 with a half-completed determination.
- Confirm field
1387matches the dev Encompass instance; swap if your config uses aCX.*custom field. - FEMA search input selectors may need adjustment after the portal re-skins; the candidate list in
playwright_assistant/capturePDF_async.pyis intentionally short. - Bump
DEFAULT_DPIfrom 150 to 200 invision_assistant/flood_zone_extractor.pyif the model misreads the zone label. - Wire
panel_number,panel_effective_date,community_name, andcommunity_idto Encompass fields once the IDs are confirmed (likely1388,1395, etc.).