Dark-themed landscape illustration showing an AI-powered email intake workflow with a glowing envelope at the center, connected to Microsoft 365, AI processing, customer data, analytics, and Zoho CRM dashboards.

How We Architected a Lead Email Intake AI Agent

Most service businesses do not realize how much revenue leaks through the inbox.

A new lead comes in. An existing customer replies to an old estimate. Someone sends photos of a damaged sofa. A designer asks about scheduling. A vendor emails accounting. A spammy agency pretends to be a potential client. Everything lands in the same inbox, and then a human has to stop what they are doing to figure out what each message actually is.

That might seem manageable at first. But once volume picks up, inbox triage becomes a hidden operational bottleneck.

That is exactly the kind of problem we like solving.

In this case, we designed a lead email intake agent for a furniture services business that needed a smarter way to process incoming emails. The objective was not just to “read” emails with AI. The real goal was to create a system that could identify new leads, separate them from general emails, detect follow-up messages from existing clients, and route each email to the right next step automatically.

For new opportunities, the system needed to capture the relevant information and push it into the CRM. For ongoing conversations, it needed to recognize that context and direct the message to the correct person or department instead of treating it like a brand-new lead.

What we are sharing here is not the exact final production product or full delivery as implemented for the client. Some logic, safeguards, and business-specific rules were adapted to their environment. But this article should give you a strong, practical model for how to implement something similar in your own business.

Why This Kind of Automation Matters More Than Most Businesses Think

When people hear “AI email automation,” they often think about inbox summaries or auto-replies. That is surface-level stuff.

The real value is in operational decision-making.

Every incoming message creates work. Someone has to decide:

  • Is this a new lead?
  • Is this an existing customer follow-up?
  • Is this a billing, support, or vendor email?
  • Does this belong in the CRM?
  • Who should handle it next?

If humans are making all of those decisions manually, the business becomes slower, more expensive to run, and far more prone to mistakes. Good leads get buried. Existing customers wait too long. Admin work clogs the sales channel. Teams waste time forwarding emails around internally.

That is why we did not approach this as a simple email classifier. We approached it as an intake and routing system.

The Problem We Were Solving

The furniture services space has a very specific kind of inbox complexity.

Customers send inquiries for upholstery cleaning, furniture repair, reupholstery, estimates, disassembly and reassembly, scheduling, and damage inspection. Many of those emails include attachments like photos, PDFs, screenshots, or forwarded threads. Some are brand-new requests. Others are replies from people already in the pipeline. And mixed in with all of that, you still have the usual noise every business receives.

So the agent had to be smart enough to do more than keyword matching.

It needed to understand context.

It had to determine whether an email was:

  • a new lead that should be saved to the CRM,
  • a follow-up from an existing lead or customer,
  • a general business email that should be routed elsewhere, or
  • junk that should be ignored.

That distinction is where most inbox automation falls apart. If your system cannot reliably tell the difference between a fresh lead and a follow-up, you end up creating duplicate CRM records, misrouting conversations, and making the inbox even messier than before.

The Core Architecture Behind the Agent

At a high level, the system followed this flow:

  1. A new email arrives in Microsoft 365.
  2. Microsoft Graph sends a webhook notification to our endpoint.
  3. We fetch the full message details and any relevant attachments.
  4. An AI agent analyzes the subject, body, sender, and attachment content.
  5. The agent classifies the email into the right category.
  6. We normalize that result into a structured payload.
  7. That payload is then sent to the correct downstream action, including Zoho CRM when applicable.

That may sound straightforward, but the difference between a toy workflow and a reliable business system is in the details.

Step 1: Setting Up Microsoft Graph Webhook Notifications

The first piece of the build was making sure the system could react to emails as they came in, instead of constantly polling the inbox.

For that, we used Microsoft Graph subscriptions. This allowed us to subscribe to new message events in a mailbox and receive webhook notifications whenever a new message was created.

That matters because polling is inefficient, slower, and harder to scale cleanly. Webhooks give you a much better event-driven architecture.

A typical Graph subscription for this type of workflow would point to an inbox or mail folder and send notifications to your webhook endpoint whenever a message is created.

{
  "changeType": "created",
  "notificationUrl": "https://yourdomain.com/webhooks/graph/messages",
  "lifecycleNotificationUrl": "https://yourdomain.com/webhooks/graph/lifecycle",
  "resource": "/users/[email protected]/mailFolders('Inbox')/messages",
  "expirationDateTime": "2026-04-05T15:30:00Z",
  "clientState": "shared-secret-value"
}

There are a few important implementation details here that people often miss:

  • Your webhook endpoint has to respond correctly to Microsoft Graph’s validation challenge.
  • Your subscription expires, so renewal logic has to be part of the design.
  • You should verify the clientState value on incoming notifications for basic security validation.
  • You want the webhook handler to stay lightweight and pass heavy work to downstream processing.

Those details are not glamorous, but they are exactly what make the difference between something that looks nice in a demo and something that survives in production.

Step 2: Pulling the Full Message and Attachment Data

The webhook notification is only the trigger. It does not usually contain everything you need for actual business processing.

So once a notification came in, the next step was to fetch the full email through Microsoft Graph. That gave us the sender information, subject line, message body, timestamps, message IDs, and attachment indicators.

Then, when attachments were present, we retrieved them as well.

This part was especially important for a furniture services workflow because attachments often contain the real signal. People attach photos of sofas, chairs, stains, damage, room layouts, prior estimates, and screenshots from previous conversations. Sometimes the body of the email is vague, but the attachment tells the real story.

That means a serious intake agent cannot look only at the email body. It should evaluate both the message itself and the useful attachment content or metadata whenever possible.

Step 3: Teaching the Agent How to Discern Email Type

This was one of the most important parts of the design.

The agent needed very clear categories. We did not want ambiguous outputs like “probably sales-related” or “looks like customer communication.” That is not actionable.

Instead, the agent needed to classify every email into a strict operational category that could drive the next system action.

Here is a sample prompt structure someone could use for this kind of workflow:

You are an intake classification agent for a furniture services company.

Analyze:
- email subject
- email body
- sender information
- attachment names
- extracted attachment text when available

Classify the email into exactly one category:
- service_request
- service_follow_up
- general_email
- discard

Rules:
1. Classify as service_request only if the message is clearly a new inquiry about furniture repair, upholstery, reupholstery, upholstery cleaning, disassembly/reassembly, estimate requests, booking, pricing, scheduling, or service availability.
2. Classify as service_follow_up only if there is clear evidence the sender is continuing an existing service-related conversation, estimate, booking, appointment, or active job.
3. Classify as general_email for operational, billing, vendor, recruiting, partnership, internal, or other non-lead business communication.
4. Classify as discard for spam, cold outreach, irrelevant sales pitches, SEO or marketing solicitations, and obvious junk.
5. Do not classify a message as service_follow_up just because the sender says “following up” unless it clearly refers to a previous furniture service conversation.
6. Attachments should support classification, but should not override the actual context of the message unless they clearly confirm it.

Return JSON with:
- category
- confidence
- reason
- customer_name
- customer_email
- customer_phone
- service_requested
- urgency
- summary
- routing_department

The reason this works well is because the prompt is grounded in operational rules, not vague intent detection. That makes the output more useful and far easier to trust downstream.

Step 4: Normalizing the Payload After AI Classification

Once the AI returned its classification and extracted fields, we normalized everything into a consistent payload format before handing it off to other systems. This step is critical.

AI output can be incredibly useful, but downstream systems like CRMs, APIs, routing logic, task creation flows, and internal logs need predictability. They do not want “creative” output. They want structure.

A normalized payload might look like this:

{
  "source": "microsoft_graph",
  "message_id": "AAMkAG...",
  "internet_message_id": "<[email protected]>",
  "thread_id": "AAQkAG...",
  "received_at": "2026-04-04T13:15:22Z",
  "from_name": "Jane Doe",
  "from_email": "[email protected]",
  "subject": "Need quote for sectional cleaning",
  "classification": "service_request",
  "confidence": 0.96,
  "routing_department": "sales",
  "summary": "Customer is requesting a quote for sectional upholstery cleaning.",
  "lead": {
    "full_name": "Jane Doe",
    "email": "[email protected]",
    "phone": "617-555-1111",
    "service_requested": "Upholstery Cleaning",
    "urgency": "Normal",
    "description": "Customer wants a quote for cleaning a sectional sofa and attached photos."
  },
  "attachments": [
    {
      "name": "sofa-photos.pdf",
      "type": "application/pdf"
    }
  ]
}

That normalized layer becomes the bridge between your AI agent and the rest of your operational stack.

It also makes troubleshooting much easier. You can log exactly what the system saw, how it classified the email, what fields were extracted, and what action was taken next.

Step 5: Sending Leads to Zoho CRM and Routing Everything Else

Once the payload was normalized, the next part was simple in theory: trigger the right business action.

In our case, brand-new leads were sent into Zoho CRM. Follow-up service emails were routed to the right person or department based on context. General emails were redirected appropriately. Junk was logged and discarded.

A sample Zoho CRM payload for a new lead might look something like this:

{
  "data": [
    {
      "First_Name": "Jane",
      "Last_Name": "Doe",
      "Email": "[email protected]",
      "Phone": "617-555-1111",
      "Lead_Source": "Email Intake Agent",
      "Description": "Customer wants a quote for sectional upholstery cleaning.",
      "Service_Requested": "Upholstery Cleaning",
      "Lead_Status": "New"
    }
  ]
}

And the routing logic conceptually looked like this:

if classification == "service_request":
    create lead in Zoho CRM
    notify sales or dispatch

elif classification == "service_follow_up":
    match by sender, CRM record, or conversation context
    route to the correct owner or department

elif classification == "general_email":
    forward or assign to admin, billing, or operations

elif classification == "discard":
    log and stop processing

That may not sound revolutionary at first glance, but operationally it is a huge upgrade. Instead of the inbox acting like one giant bucket, it becomes an intelligent intake channel.

What Actually Makes a System Like This Work in the Real World

This is the part people often underestimate.

The AI classifier is only one piece. What makes the overall system dependable is the business logic and safeguards around it.

Some of the most important considerations include:

  • Dedupe logic so the same message is not processed multiple times.
  • CRM lookups to help determine whether a message is truly a new lead or a follow-up.
  • Validation rules before creating CRM records so you do not pollute your database.
  • Logging and audit trails for every classification and downstream action.
  • Queue or worker-based processing so your webhook endpoint stays fast and reliable.
  • Subscription renewal logic so Microsoft Graph notifications do not silently stop.

That is the difference between building automation that sounds smart and building automation that the business can actually trust.

The Bigger Lesson for Service Businesses

The real takeaway here is not “use AI to read emails.” It is this:

Build systems that remove decision clutter from your operations.

A good intake agent does not just summarize inbound messages. It reduces manual sorting, shortens response times, protects lead quality, improves CRM accuracy, and makes the handoff between teams much cleaner.

For service businesses especially, that matters a lot. The speed and accuracy of your intake process directly affect conversion, customer experience, and internal efficiency.

If a new lead sits in the inbox too long, that is not just an admin problem. That is a revenue problem.

Final Thoughts

This project is a good example of how AI becomes genuinely valuable when it is embedded into a real business workflow, not just layered on top as a novelty.

We did not set out to build a flashy AI feature. We set out to solve a costly operational problem: too much human time being wasted on inbox triage, too much room for error, and too much friction between incoming communication and the systems that should act on it.

By combining Microsoft Graph webhooks, structured AI classification, normalized payload design, and Zoho CRM integration, we created a framework for turning email into a cleaner, smarter intake channel.

Again, this is not the exact final production system delivered, and some of the actual implementation details were tailored to the client’s internal processes, routing rules, and CRM structure. But if you are trying to build something similar, this should give you a strong picture of the architecture, logic, and workflow design behind it.

And honestly, that is the bigger point: businesses do not need more disconnected tools. They need smarter systems that reduce noise, improve decision speed, and create operational leverage.

That is where this kind of automation really shines.