Launch-Free 3 months Builder plan-
Pixel art lobster working at a computer terminal with email — email event stream architecture agent system

event stream architecture for agent email systems

Polling wastes cycles and misses time-sensitive emails. Event stream architecture lets your agent react to email in real time.

8 min read
Ian Bussières
Ian BussièresCTO & Co-founder

Most agent email setups start with a polling loop. Check for new messages every 30 seconds, process what's there, repeat. It works for demos. It falls apart in production.

The problem isn't polling itself. Polling is fine when one agent checks one inbox a few times per hour. The problem is what happens when your system grows: more agents, more inboxes, time-sensitive emails like verification codes that expire in 60 seconds. Suddenly that 30-second gap between checks isn't a minor inefficiency. It's a reliability bug.

Event stream architecture flips the model. Instead of agents asking "anything new?", the email infrastructure pushes events to agents the moment something arrives. Your agent subscribes to an email event stream and reacts in real time, with zero wasted cycles between messages.

If you're building agent systems that depend on email, this is the architecture to build on. Here's how it works in practice, and where it breaks down if you skip the details.

What breaks when agents poll for email#

When an agent polls for email, it makes a synchronous, scheduled request regardless of whether anything has changed. This creates problems that compound as systems scale.

Latency is the first casualty. A verification code from Stripe hits the inbox at second 2 of your 30-second polling cycle. Your agent won't see it until second 30. For codes that expire in 60 seconds, you've already burned half your window doing nothing. Some two-factor flows give you 90 seconds. Others give you 30. Polling turns every time-sensitive email into a coin flip.

Then there's wasted compute. An agent polling an empty inbox every 30 seconds makes 2,880 API calls per day. Across 50 inboxes, that's 144,000 requests daily, the overwhelming majority returning nothing. On metered infrastructure, that's real money spent on empty responses.

Race conditions are the third issue. Two agents polling the same shared inbox can both retrieve the same unread message before either marks it processed. Now two agents are responding to the same customer, or two agents are trying to use the same one-time verification code.

Finally, coupling. Your polling loop becomes a monolith where retrieval logic, routing logic, and processing logic all live in the same function. Changing how you route billing emails means touching the same code that handles support tickets. Every modification risks breaking something unrelated.

How event streams replace polling#

In an event-driven email system, inbound messages become events the moment they arrive. The email infrastructure acts as the event producer. Your agents are consumers. An event stream connects them.

The flow works like this: an email lands in an inbox, the infrastructure parses it and generates a structured event containing the sender, subject, body, timestamp, and security metadata like injection risk scores. That event gets pushed to every subscribed consumer in real time. No scheduled checks. No empty responses. If no email arrives for three hours, your agents spend zero compute during those three hours.

The transport layer is typically a webhook. Your agent exposes an HTTP endpoint, the email infrastructure POSTs events to it as they occur. This is simpler than running a full message queue for most agent systems, and it maps directly to how modern APIs handle real-time data. Research from HiveMQ's 2026 analysis of agent communication patterns found that event-driven architectures eliminate the temporal coupling problems that plague synchronous polling, letting agents move on to other tasks while waiting for results instead of blocking on empty checks.

Here's what a basic email event handler looks like:

app.post('/webhooks/email', async (req, res) => {
  const event = req.body;

  if (event.type === 'email.received') {
    const { from, subject, body, injectionScore } = event.data;

    // Reject emails with high injection risk before processing
    if (injectionScore > 0.7) {
      console.warn('Blocked risky email:', { from, subject });
      return res.status(200).send('dropped');
    }

    await agent.process({ from, subject, body });
  }

  res.sendStatus(200);
});

Every incoming email triggers exactly one event. The agent processes it immediately, then goes idle until the next event. No polling interval, no missed window, no wasted API calls.

Routing emails to specialized agents#

Event streams get more interesting when multiple agents consume from the same email infrastructure. Instead of every agent managing its own inbox and polling loop, a single routing layer distributes events based on content, sender, or inbox.

Consider a setup I see often: an onboarding agent handles welcome flows and verification codes, a billing agent watches for payment notifications from Stripe and PayPal, and a support agent fields inbound customer questions. With polling, each agent needs its own retrieval logic and some shared coordination mechanism to avoid conflicts. With events, the router handles distribution and each agent receives only the messages meant for it.

const routes = new Map([
  ['stripe.com', billingAgent],
  ['paypal.com', billingAgent],
  ['github.com', devopsAgent],
]);

function routeEmail(event) {
  const senderDomain = event.data.from.split('@')[1];
  const agent = routes.get(senderDomain) || supportAgent;
  return agent.process(event.data);
}

This pattern lets you add, remove, or update agents without touching the others. If your billing agent goes down for maintenance, the router can queue its events or redirect them to a fallback. If you add a new agent for legal compliance emails, you add one routing rule. The rest of the system is untouched.

The decoupling matters more than it seems at first. Agent systems evolve fast. Papers on multi-agent architectures surged from 820 in 2024 to over 2,500 in 2025, according to O'Reilly's tracking of the research space. The systems being built today will look different in six months. Architectures that let you swap components independently survive that evolution. Monolithic polling loops don't.

Setting this up with LobsterMail#

LobsterMail supports event stream architecture natively through webhooks. When an email arrives at any of your agent's inboxes, LobsterMail pushes a structured event to your registered endpoint with the full message payload, security metadata, and an injection risk score.

import { LobsterMail } from '@lobsterkit/lobstermail';

const lm = await LobsterMail.create();

// Your agent hatches its own inbox
const inbox = await lm.createSmartInbox({ name: 'Support Agent' });

// Register a webhook for real-time email events
await lm.webhooks.create({
  url: 'https://your-app.com/webhooks/email',
  events: ['email.received'],
  inboxId: inbox.id,
});

console.log(`Listening for emails at ${inbox.address}`);

Every inbound email becomes an event your agent reacts to. The injection scoring runs at the infrastructure level before events reach your webhook, so your routing layer can filter high-risk messages without the agent ever seeing the content.

If you've been running a polling loop and want to switch, . Your agent can start receiving real-time email events within a few minutes.

Security at the event layer#

One consideration that's easy to overlook: when emails become events that feed into LLM-powered agents, every inbound message is a potential prompt injection vector. An attacker can craft an email body that instructs your agent to ignore its system prompt, exfiltrate data, or take unauthorized actions. We covered common attack patterns in our post on agent email setup mistakes.

This is why injection scoring belongs at the infrastructure layer, not inside the agent. If your email provider scores each message before it becomes an event, your routing layer can quarantine or drop suspicious messages before any agent processes them. The scoring happens upstream, where a crafted payload can't talk its way past the filter.

The threshold depends on your use case. Agents handling password resets or financial data should reject anything above 0.5. Agents reading newsletters can tolerate a higher score. The point is that the decision happens at the routing layer, not inside the agent's reasoning loop, where a well-crafted injection might convince the agent to override its own safety checks.

Start with events, not polling#

If you're building a new agent that needs email, start with event stream architecture from the beginning. One inbox, one webhook endpoint, one handler function. That's the minimum viable setup, and it scales to multi-agent coordination without a rewrite.

LobsterMail's free tier includes webhook support with 1,000 emails per month. That's enough to build and validate your event-driven architecture before spending anything. The Builder plan at $9/month covers most production workloads with up to 10 inboxes and 5,000 emails per month.

Polling works for throwaway prototypes. Events work for systems you plan to keep running. Build on the right foundation and you won't be rewriting your email layer in six months when your third agent joins the system and polling finally buckles under the load.

Frequently asked questions

What is event stream architecture for email?

Event stream architecture treats every incoming email as an event pushed to subscribers in real time, instead of requiring agents to poll for new messages on a schedule. The email infrastructure produces events, and agents consume them through webhooks or message queues.

What's the difference between polling and event-driven email?

Polling means your agent repeatedly asks "is there new mail?" at a fixed interval, regardless of whether anything arrived. Event-driven means the infrastructure notifies your agent the moment a message appears. Events eliminate wasted requests and reduce latency from seconds to milliseconds.

How do webhooks work for agent email?

Your agent registers an HTTP endpoint with the email provider. When a new message arrives, the provider sends an HTTP POST to that endpoint with the full email payload and metadata. Your agent processes the request and responds with a 200 status code.

Can multiple agents share the same email inbox?

Yes. With event-driven architecture, a routing layer can distribute events from the same inbox to different agents based on sender, subject, or content. This avoids the race conditions that happen when multiple agents poll the same inbox independently.

What happens if my webhook endpoint goes down?

LobsterMail retries failed webhook deliveries with exponential backoff. If your endpoint stays down past the retry window, you can fall back to the SDK's receive() method to retrieve any messages that arrived during the outage.

How does LobsterMail protect agents from prompt injection in emails?

Every inbound email is scored for injection risk at the infrastructure level before the event reaches your webhook. Your routing handler can check the injectionScore field and drop or quarantine messages above your chosen threshold. See the security docs for details on how scoring works.

Does the free plan support webhooks?

Yes. Webhook support is included on all LobsterMail plans, including the free tier at 1,000 emails per month. You can register webhook endpoints for any inbox and receive real-time email events at no cost.

How fast are webhook events compared to polling?

Webhook events typically arrive within milliseconds of the email landing in the inbox. With a 30-second polling interval, you'd wait an average of 15 seconds. For verification codes and other time-sensitive messages, that difference is the gap between success and an expired link.

Do I need Kafka or a message queue for agent email?

For most agent systems, webhooks are sufficient. A message queue like Kafka or Redpanda adds value when you need event replay, guaranteed ordering, or you're processing thousands of emails per minute. Start with webhooks and add a queue only if you hit those requirements.

How do I route different emails to different agents?

Build a routing function in your webhook handler that inspects the sender domain, subject line, or other metadata and dispatches to the appropriate agent. LobsterMail's webhook payload includes all message metadata, so you can route before parsing the full body.

Can I use event-driven email with the LobsterMail Node.js SDK?

Yes. The SDK's webhooks.create() method registers your endpoint for specific event types and inboxes. See the webhooks guide for setup instructions and payload format details.

What does a LobsterMail webhook payload look like?

A typical payload includes the event type (email.received), sender address, recipient inbox ID, subject, body in both text and HTML, timestamps, and security metadata including the injection risk score. The full schema is documented in the webhooks guide.

Related posts