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.
The shape you want
{"source": "portal_a", "received_at": "2026-10-19T09:12:00+05:30", "name": "...", "phone": "...", "email": "...", "listing_ref": "...", "message": "...", "raw_id": "<message-id>"} One parser per sender
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 Validate before you store
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.
Rules that save pain later
- Keep the raw email ID so you can re-parse when a portal changes its format, which happens without notice.
- Normalise phone numbers to one format before matching against existing contacts.
- Fail loudly. A parser that silently returns
Nonefor the phone number creates leads nobody can call. - Use AI only for the free-text message. Pulling budget, move-in date or "wants to view this weekend" from the message is a good AI job. Store that output in separate fields so it never overwrites what you parsed deterministically.
- Write a test per portal using a saved real email with personal details removed.
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.