
ai agent email webhook vs polling: which one actually works
Webhooks and polling both get email to your AI agent, but they solve different problems. Here's when to use each and why most agents need both.
Your AI agent needs to know when an email arrives. There are two ways to find out: wait for a notification (webhook) or keep checking (polling). The choice shapes everything from response latency to how much you spend on API calls each month.
Most developers pick one and stick with it. For AI agents, that's usually the wrong call. Here's why.
Webhook vs polling at a glance#
| Dimension | Webhook | Polling |
|---|---|---|
| Latency | Sub-second, event-driven | 15–60 seconds depending on interval |
| API cost | Near zero (provider pushes to you) | Scales with frequency and inbox count |
| Complexity | Requires a public HTTPS endpoint | Simple loop, no inbound infrastructure |
| Reliability | Depends on your uptime and retry handling | Self-healing (next poll catches missed events) |
| Best for | Real-time agent responses, thread replies | Batch processing, serverless cold-start tolerance |
For most AI agent email workflows, webhooks win on speed and cost. Polling wins on simplicity and resilience. The best production systems use both.
How webhooks work for AI agent email#
A webhook is a reverse API call. Instead of your agent asking "any new email?" every 30 seconds, the email provider sends an HTTP POST to your agent's endpoint the moment something happens. New email received, message sent, bounce detected. Your agent reacts immediately.
Here's what a typical webhook registration looks like with LobsterMail:
const webhook = await lm.createWebhook({
url: 'https://your-agent.example.com/hooks/email',
events: ['email.received', 'email.bounced', 'email.thread.reply'],
});
The payload arrives with the email summary, thread context, and security scores already attached. Your agent doesn't need a second API call to figure out what happened. It gets the sender, subject, preview text, thread ID, and injection risk score in one shot.
This matters for agent response time. A customer support agent that processes replies in under a second feels qualitatively different from one that takes 30-45 seconds. That gap is the difference between a conversation and a form submission.
How polling works (and when it's fine)#
Polling is straightforward. Your agent calls the inbox API on a timer, checks for new messages, and processes anything it finds.
// Simple polling loop
setInterval(async () => {
const emails = await lm.getEmails({
inboxId: 'ibx_xyz789',
since: lastChecked
});
for (const email of emails) {
await agent.process(email);
}
lastChecked = new Date();
}, 30000); // every 30 seconds
For batch workflows where latency doesn't matter (daily digest processing, weekly report generation, periodic lead qualification), polling is perfectly reasonable. It requires no public endpoint, no HTTPS certificate, no firewall rules. Your agent just runs a loop.
The problem shows up at scale. An agent managing 50 inboxes with 30-second polling makes 144,000 API calls per day. Most of those calls return nothing. With webhooks, you get zero calls when nothing happens and one notification per actual event.
The cold-start problem with serverless agents#
Here's a scenario none of the common guides address well. Your AI agent runs on a serverless platform. A webhook fires. The platform spins up your function. But the function needs 2-4 seconds to initialize, load its model context, and become ready. The webhook request times out.
Three ways to handle this:
-
Queue buffer. Put a lightweight queue (SQS, Redis, or even a simple HTTP relay) between the webhook and your agent. The queue accepts the webhook instantly and your agent pulls from it when ready.
-
Retry tolerance. LobsterMail retries failed webhook deliveries with exponential backoff. If your function warms up by the second attempt, the event lands safely.
-
Hybrid pattern. Accept webhooks when your agent is warm. Run a polling sweep on cold start to catch anything that arrived while the function was down. This is the most resilient approach for serverless deployments.
Why each agent needs its own inbox and webhook endpoint#
When multiple agent instances share a single inbox, you get race conditions. Two agents see the same email, both try to reply, and your customer gets a duplicate response. Or worse, one agent processes the email and the other never knows, leaving its conversation state out of sync.
The cleaner architecture: one inbox per agent instance, one webhook endpoint per inbox. Each agent owns its own email identity, its own conversation threads, and its own event stream. No coordination required. No deduplication logic. No shared state.
This is why LobsterMail provisions inboxes per agent rather than per account. Your agent hatches its own inbox, registers its own webhook, and operates independently. If you're running five agents handling different customer segments, each one has isolated email infrastructure with its own event pipeline.
Making webhooks reliable: idempotency and verification#
Two things go wrong with webhooks in production. Events arrive more than once (at-least-once delivery), and bad actors send fake events to your endpoint.
Handling duplicate deliveries#
Every webhook event should include an idempotency key, typically the event ID or a combination of event type and resource ID. Before processing, check if you've seen this key before:
async function handleWebhook(payload) {
const key = `${payload.event}:${payload.data.emailId}`;
if (await store.has(key)) return; // already processed
await store.set(key, true, { ttl: 86400 });
await agent.process(payload);
}
Verifying webhook signatures#
LobsterMail signs every webhook payload with the secret returned during creation. Verify the HMAC signature before processing:
import { createHmac } from 'crypto';
function verifySignature(payload, signature, secret) {
const expected = createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return signature === expected;
}
Without signature verification, anyone who discovers your webhook URL can trigger your agent with fabricated email events. For an autonomous agent, that's not just a nuisance. It's a prompt injection vector. Someone could craft a fake "email received" event with malicious content in the preview field and trick your agent into acting on it.
Thread context across multiple webhook events#
A single customer conversation might span ten webhook events: the initial email, your agent's reply, the customer's follow-up, another reply, and so on. Each email.thread.reply event includes a threadId that ties these together.
Your agent should maintain thread state keyed by threadId. When a new reply event arrives, load the thread history, append the new message, and let the agent generate its response with full context. This is how agents maintain coherent multi-turn conversations over email without losing track of what was discussed three messages ago.
LobsterMail's webhook payloads include the thread ID on every email event, so your agent never has to guess which conversation a message belongs to.
Filtering noisy events#
Not every webhook event needs agent attention. Bounces and complaints are operational signals, not conversation events. Route them to a monitoring pipeline instead of your agent's main processing loop:
async function routeEvent(payload) {
switch (payload.event) {
case 'email.received':
case 'email.thread.reply':
return agent.processConversation(payload);
case 'email.bounced':
return monitoring.recordBounce(payload);
case 'email.quarantined':
return monitoring.flagQuarantined(payload);
default:
return; // ignore unhandled events
}
}
This keeps your agent focused on conversations while still capturing delivery health data for diagnostics.
The hybrid approach most production agents use#
The pattern that works best in practice: webhook-first with a polling fallback.
Webhooks handle the real-time path. Your agent responds to emails within seconds. But if your webhook endpoint goes down for five minutes (deployment, crash, network blip), those events are lost after the retry window closes.
A lightweight polling job runs every few minutes and reconciles. It checks for any emails that arrived but weren't processed via webhook. Think of it as a safety net, not the primary mechanism. This way you get sub-second response times during normal operation and zero data loss during outages.
For agents built on LobsterMail, this pattern is straightforward. The webhook delivers events in real time, and the inbox API lets you query for messages by timestamp range to catch anything the webhook missed.
If you want your agent handling email with this kind of reliability, . Paste the instructions to your agent and it sets up the inbox, webhook, and polling fallback on its own.
Frequently asked questions
What is the main difference between webhooks and polling for AI agent email?
Webhooks push events to your agent instantly when something happens. Polling requires your agent to repeatedly ask "anything new?" on a timer. Webhooks are faster and cheaper at scale, while polling is simpler to implement.
Does polling cause noticeable delays in AI agent email workflows?
Yes. With a typical 30-second polling interval, your agent takes an average of 15 seconds to notice a new email. For customer-facing agents, that latency makes conversations feel sluggish compared to the sub-second response webhooks enable.
How do webhooks reduce API costs compared to polling?
Polling generates API calls whether or not new email exists. An agent polling 50 inboxes every 30 seconds makes 144,000 calls per day, mostly returning empty responses. Webhooks only fire when an event actually occurs, reducing API usage to a fraction of that.
What email events should an AI agent subscribe to via webhooks?
At minimum, subscribe to email.received and email.thread.reply for conversation handling. Add email.bounced for delivery monitoring and email.quarantined if your agent needs to know about flagged messages. Only subscribe to events your agent will act on.
Can a webhook and polling strategy be combined?
Yes, and most production agents do exactly this. Webhooks handle real-time event delivery, while a polling job runs every few minutes to catch any events missed during webhook endpoint downtime. This gives you speed and reliability together.
What happens if my AI agent's webhook endpoint is temporarily down?
LobsterMail retries failed webhook deliveries with exponential backoff. If your endpoint recovers within the retry window, events are delivered normally. For longer outages, a polling fallback ensures no emails are missed permanently.
How do I make webhook delivery idempotent for an AI agent?
Store the event ID or a composite key (event type + resource ID) after processing each webhook. Before processing a new event, check if that key already exists. This prevents your agent from replying to the same email twice when a webhook is retried.
Should each AI agent instance have its own email inbox and webhook endpoint?
Yes. Shared inboxes create race conditions where multiple agents process the same email. Giving each agent its own inbox and webhook endpoint eliminates deduplication complexity and keeps conversation state isolated.
How do I verify that an inbound webhook actually came from my email provider?
Verify the HMAC signature included with each webhook request using the secret provided during webhook creation. Compare the computed SHA-256 hash of the payload body against the signature header. Reject any request that doesn't match.
How does email thread context get passed to an AI agent via webhook payload?
Each webhook event includes a threadId field that groups related messages into a conversation. Your agent loads the thread history by this ID, appends the new message, and generates a response with full conversational context.
Is polling ever the right choice over webhooks for AI agent email?
Yes. For batch processing (daily digests, weekly reports), environments without a public HTTPS endpoint, or simple prototypes where sub-second latency doesn't matter, polling is simpler and requires no inbound infrastructure.
How do serverless AI agents handle webhooks that arrive before the function is warm?
Place a lightweight queue between the webhook and your serverless function. The queue accepts the webhook instantly, and your function pulls events once it's initialized. Alternatively, rely on webhook retries to land on the second attempt after the function warms up.
What latency difference can I expect between webhooks and 30-second polling?
Webhooks typically deliver events in under one second. With 30-second polling, average latency is 15 seconds (half the interval) and worst case is the full 30 seconds. For real-time agent interactions, that gap is immediately noticeable to end users.
How do I filter noisy email events before they reach my AI agent?
Route events by type in your webhook handler. Send conversation events (email.received, email.thread.reply) to your agent's processing logic. Route operational events (email.bounced, email.quarantined) to a monitoring pipeline instead.
Can LobsterMail send webhooks for both inbound and outbound email events?
Yes. You can subscribe to email.received for inbound messages and email.sent for outbound confirmations. The email.bounced event covers failed outbound deliveries. Subscribe to any combination of events when creating the webhook.


