Skip to main content

Your AI Support Agent Needs a Real Inbox, Not a Webhook

· 7 min read
Founder, mailbot

Every team building an AI support agent has the same week two moment. The webhook is working. Messages are flowing in. Then a customer CCs their manager, and everything breaks.

You know the story because you've probably lived it. The AI support agent is up. It can read inbound messages and generate replies. The demo is impressive. Leadership is excited. The webhook endpoint is parsing emails, the LLM is generating responses, and tickets are getting resolved without a human touching them.

Then production happens.

The first week versus the second week

Week one is clean. Every inbound message is a fresh email. One sender. One subject line. One body. Your webhook receives a POST, you extract the text, pass it to your model, and fire back a reply. It works. You feel smart.

Week two is when reality introduces itself.

A customer replies to a thread, but their email client strips the In-Reply-To header. Now your system treats it as a brand new conversation. The customer gets a fresh "Thanks for reaching out!" response to a ticket they opened three days ago.

Another customer CCs their manager on a reply. Your webhook receives two events for what is logically one message. Your agent responds twice. The manager, who was just added for visibility, now receives a duplicate response from a bot.

A third customer forwards the original email to a colleague, who replies directly. The reply arrives with different headers, a different sender, and no reference to the existing thread. Your system creates a third ticket for the same issue.

None of these are unusual. This is just what email looks like when real people use it.

The duct tape stack

What every team builds next is remarkably similar. I've seen this pattern at companies of all sizes, and the sequence is almost always the same.

First, you build a threading layer. Your webhook gives you raw messages with no thread context. So you start matching messages by subject line. That works until someone changes the subject. Then you add header matching on Message-ID, In-Reply-To, and References. That works until an email client modifies the References chain. Then you add sender/recipient pair matching as a fallback. You're now maintaining a state machine for email threading that you never planned to build.

Then, you build deduplication. CC recipients generate multiple inbound events for the same message. Forwarded emails can trigger the webhook more than once if retries are configured. Without deduplication, your agent processes the same message multiple times and sends multiple replies. So you add a dedup layer, usually based on Message-ID hashes. Except some email clients generate non-unique Message-IDs. So you add content hashing as a fallback.

Then, you build bounce handling. An agent replies to a customer, but the email bounces. Without bounce detection, your system marks the ticket as "responded" and moves on. The customer never got a response. Nobody knows. So you add bounce processing, which means parsing bounce notification emails (which have their own format inconsistencies) and mapping them back to the original outbound message.

Then, you build retry logic. Your webhook endpoint was down for thirty seconds during a deploy. Seven inbound emails were lost. You didn't know until a customer complained that they never heard back. So you add a queue in front of the webhook, or you start polling for messages, or you build some hybrid of both.

You're now maintaining a significant amount of email infrastructure. None of it is your product. All of it is necessary for your product to work.

A webhook is not an inbox

Here's the core issue that most teams discover too late: a webhook gives you events, not state.

When a webhook fires, you receive a snapshot. A single message, delivered as a POST request to your endpoint. What happened before that message? What thread does it belong to? What other messages are in that conversation? Who else is involved? What's the full history?

The webhook doesn't know. That's not its job. A webhook is a notification mechanism. It tells you that something happened. It doesn't give you the context to understand what that thing means.

An inbox gives you state.

With an inbox, you don't reconstruct threads from individual webhook events. You query the thread and get the full conversation. You don't guess whether a message is a reply or a new conversation. The system has already made that determination based on email headers and stored the result. You don't wonder whether a message was delivered or bounced. The inbox has the event timeline.

This distinction matters because AI support agents need context to work. When a customer sends their third message in a thread, the agent needs to read the full conversation history before responding. If your system represents conversations as a series of disconnected webhook events, your agent has to reassemble the context every time. If your system represents conversations as threads with message history, the agent just reads the thread.

One of these is fragile. The other is infrastructure.

What this looks like in mailbot

Let me make this concrete. In mailbot, setting up an AI support agent starts with creating an inbox and registering a webhook. But the webhook isn't the system of record. The inbox is.

Create an inbox for your support agent:

curl -X POST https://getmail.bot/v1/inboxes \
-H "Authorization: Bearer mb_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"username": "support",
"display_name": "Support Agent",
"domain": "mailbot.id"
}'

Register a webhook so your system knows when a new message arrives:

curl -X POST https://getmail.bot/v1/webhooks \
-H "Authorization: Bearer mb_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.example.com/webhooks/support",
"events": ["message.received"]
}'

When the webhook fires, you don't need to parse raw MIME data or reconstruct thread state. You call the threads API to get the full conversation:

curl https://getmail.bot/v1/threads/{thread_id}/messages \
-H "Authorization: Bearer mb_live_xxxxxxxxxxxx"

This returns every message in the thread, in order, with metadata. Your agent reads the thread, generates a response, and sends the reply:

curl -X POST https://getmail.bot/v1/messages/send \
-H "Authorization: Bearer mb_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"inbox_id": "inbox_01hrz...",
"thread_id": "thr_01hrz...",
"to": ["customer@example.com"],
"subject": "Re: My account is locked",
"body_text": "I have reset your account. You should be able to log in now."
}'

mailbot handles the threading headers. The reply appears in the correct thread in the customer's email client. The full conversation is stored and queryable. No reconstruction needed.

The debugging problem nobody talks about

There's a second cost to the webhook approach that only becomes visible in production: debugging.

When a customer says "I never got a response to my email," you need to trace the full lifecycle. Did the inbound message arrive? Did the webhook fire? Did your agent process it? Did the agent send a reply? Was the reply delivered? Was it bounced?

In a webhook-based system, this investigation spans multiple services. Check the email provider's inbound logs. Check your webhook receiver logs. Check your application logs. Check your outbound email provider's delivery logs. Hope the timestamps line up. Hope nothing got dropped between services.

In mailbot, you query the inbox. The thread has the full message history. Each message has an event timeline showing when it was received, processed, sent, delivered, or bounced. It's one system. One place to look. One source of truth.

This isn't a feature for marketing slides. This is the difference between spending five minutes resolving a customer escalation and spending two hours chasing logs across four services while the customer waits.

The pattern is the problem

If you're building an AI support agent and you're currently maintaining custom threading logic, deduplication filters, bounce handlers, and retry mechanisms on top of a webhook, I want you to consider something.

All of that code exists because your email integration doesn't have the concept of an inbox. It has events. Individual, disconnected events. And you're rebuilding what an inbox provides, one workaround at a time.

You don't need a better webhook. You need a real inbox. One that receives email, threads conversations, tracks message state, and gives your agent the context it needs to do its job.

That's what mailbot was built for.


mailbot is programmable email infrastructure. Read the docs · Get API key