cd /news/ai-agents/parsing-property-portal-lead-emails-… · home › topics › ai-agents › article
[ARTICLE · art-152715] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=· neutral

Parsing Property Portal Lead Emails Into Clean Records Before Any AI Touches Them

The operator of NxFlowAI, an AI automation agency, outlined a per-sender email parsing approach for converting property portal lead notifications into a uniform record schema before any AI follow-up is added. The method routes messages by the From header to portal-specific regex parsers, then validates that a listing reference and at least one contact channel are present, sending failures to a human with a reason attached rather than guessing. The agency says this intake mapping is part of a first audit that takes 72 hours per workflow.

by read1 min views1 publishedOct 19, 2026

Many property portals still deliver leads to small agencies as notification emails. Before you add an AI assistant to the follow-up, turn those emails into clean, structured records. Clean input makes everything downstream simpler, from dedupe to follow-up timers.

This intake step is the unglamorous start of real estate lead automation for small agencies, and it is worth getting right.

{"source": "portal_a", "received_at": "2026-10-19T09:12:00+05:30",
 "name": "...", "phone": "...", "email": "...",
 "listing_ref": "...", "message": "...", "raw_id": "<message-id>"}

Each portal formats its emails differently, so write one small parser per sender address and route by the From header.

import re

def parse_portal_a(body: str) -> dict:
    def grab(pattern):
        m = re.search(pattern, body)
        return m.group(1).strip() if m else None
    return {
        "name": grab(r"Name:\s*(.+)"),
        "phone": grab(r"Phone:\s*([+\d][\d\s-]+)"),
        "email": grab(r"Email:\s*(\S+@\S+)"),
        "listing_ref": grab(r"Ref(?:erence)?:\s*(\S+)"),
        "message": grab(r"Message:\s*([\s\S]+?)\n\s*\n"),
    }

PARSERS = {"leads@portal-a.example": parse_portal_a}

def parse(email):
    parser = PARSERS.get(email.sender)
    if parser is None:
        return route_to_human(email, reason="unknown_sender")
    record = parser(email.text_body)
    record.update(source=email.sender, raw_id=email.message_id,
                  received_at=email.date)
    return record
python
REQUIRED = ("listing_ref",)
ONE_OF = ("phone", "email")

def validate(r):
    problems = [f for f in REQUIRED if not r.get(f)]
    if not any(r.get(f) for f in ONE_OF):
        problems.append("no_contact")
    return problems

If validate returns anything, send the email to a person with the reason attached. Do not guess.

None for the phone number creates leads nobody can call. Dedupe and follow-up timers come after this step. They are much easier when every lead arrives in the same shape.

Disclosure: I run NxFlowAI, an AI automation agency. Mapping intake like this is part of our first audit, which takes 72 hours for one workflow.

── more in #ai-agents 4 stories · sorted by recency
── more on @nxflowai 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/parsing-property-por…] indexed:0 read:1min 2026-10-19 · —