Skip to main content

Api

The AgentMail webhook bug that only fires when a message has attachments

An inbox webhook handler that works perfectly in every test — then silently drops real customer emails with PDFs attached. It's planted in our open-source playground. Reproduce it from your IDE.

FetchSandbox EngineeringFetchSandbox Engineering

There is a webhook bug shape that passes every test you write by hand: the handler that works for the messages you send it, and silently drops the messages your customers actually send.

We planted one in github.com/fetchsandbox/playground, in apps/agentmail — a small FastAPI service that receives AgentMail inbox webhooks and routes inbound messages to an agent queue.

The flaw: the message.received handler processes plain messages fine, but ignores events when the message body carries attachments. Every test email you send during development is plain text, so the queue fills, the dashboard looks healthy, and the integration ships. Then a customer sends the signed contract as a PDF, the event arrives, and the handler drops it on the floor. No error. No retry alarm. Just a message your agent never saw.

Why this bug survives code review

The handler reads naturally:

@app.post("/webhooks/agentmail")
async def inbox_webhook(event: dict):
    if event["type"] == "message.received":
        message = event["data"]["message"]
        if message.get("text"):
            await queue_for_agent(message["text"])
    return {"ok": True}

Nothing here looks wrong in isolation. The reviewer sees "if there's text, queue it" and moves on. The failure only exists in the space of inputs nobody generated locally: multipart messages where the payload shape differs, text is empty and the content lives in parts, or the attachment changes which fields are present.

Mocks make this worse, not better. A mock returns the payload you told it to return — and you told it to return the shape you already handle.

Reproduce it from your IDE

The playground app ships a .mcp.json wired to FetchSandbox's hosted AgentMail sandbox. Open it in Cursor or Claude Code and paste:

./fetchsandbox inbound emails with attachments seem to vanish before they
reach the agent queue — audit the agentmail inbox webhook handling

The bar for a real catch, same as every playground task:

  • the drop is reproduced — an attachment-bearing message.received event goes in, nothing reaches the queue
  • the proof shows buggy vs fixed behavior on the webhook route
  • there is a receipt URL — an openable run trace, not a claim in chat

No AgentMail account, no API keys. The sandbox models the inbound lifecycle including the payload shapes that differ between plain and multipart messages.

The general rule

Inbox webhooks are not one event type; they are a family of payload shapes that share an event name. If your handler branches on a field that attachments change, you have two integrations — and you only tested one.

Before wiring an inbound-email agent to production, run the message lifecycle with attachments, replies, and forwards, not just the clean text message your dev script sends. That is exactly the kind of workflow the sandbox runs in one prompt from your IDE.

Start with TESTING.md in the playground — or the AgentMail sandbox docs.