Skip to content
Engineering

Turn Inbound Email Into Structured Data for Agents

| | 14 min read
A cyberpunk night scene in a rain-slick data center corridor, with a single glowing inbox event stream suspended above a reflective floor as the hero element. The stream flows through a signed webhook checkpoint, a JSON normalization node, and an artifact validation panel, then resolves into a small agent-ready result card. Electric cyan, hot magenta, deep purple, and warm orange reflections shimmer across wet metal surfaces and glass, with holographic timestamps, subtle circuitry, drifting fog, and visible light rays cutting through haze. The composition is wide and cinematic with strong depth, noir shadows, and organic edges fading into smoke and black.
A cyberpunk night scene in a rain-slick data center corridor, with a single glowing inbox event stream suspended above a reflective floor as the hero element. The stream flows through a signed webhook checkpoint, a JSON normalization node, and an artifact validation panel, then resolves into a small agent-ready result card. Electric cyan, hot magenta, deep purple, and warm orange reflections shimmer across wet metal surfaces and glass, with holographic timestamps, subtle circuitry, drifting fog, and visible light rays cutting through haze. The composition is wide and cinematic with strong depth, noir shadows, and organic edges fading into smoke and black.

Inbound email is one of the most useful signals an AI agent can receive. It can confirm a signup, deliver an OTP, notify a workflow, send a receipt, or trigger a support action. The problem is that email was designed for people and mail clients, not for autonomous software that needs stable inputs and bounded actions.

Raw email is a mix of headers, MIME parts, encodings, HTML, quoted replies, forwarded content, and sometimes duplicate deliveries. The message format itself is defined across standards such as RFC 5322 and MIME, which is powerful but not agent-friendly. If you hand that entire blob to an LLM, you get unnecessary tokens, brittle parsing, and a much larger attack surface.

The better pattern is to turn inbound email into structured data before it reaches the agent. In practice, that means receiving messages through a programmable inbox, normalizing them into JSON, extracting the smallest useful artifact, and giving the model a safe, typed view of what happened.

Why raw inboxes break agent workflows

A human mailbox is built around browsing, searching, and reading. An agent workflow is built around state transitions. Those are different problems.

When an agent waits for a verification email, it does not need the full inbox. It needs to know whether the expected message arrived, whether it belongs to the current task, and which OTP or magic link should be used. When an agent monitors an operational inbox, it does not need every tracking pixel, footer, and quoted reply. It needs a normalized event with provenance, confidence, and constraints.

Raw inbox access creates several recurring problems:

  • Ambiguous selection, because old messages, duplicate messages, and retries can look similar.
  • Fragile parsing, because HTML layouts and email templates change often.
  • Excessive context, because raw MIME and HTML consume tokens without improving decisions.
  • Weak security boundaries, because inbound content can include prompt-injection text, malicious links, or misleading instructions.
  • Poor observability, because it is hard to debug why a model chose one message or link over another.

For agents, the safest prompt is often no prompt at all. A deterministic tool should parse the message, validate the artifact, and return a compact result such as found OTP 123456 for inbox ibx_123 from delivery del_456. The LLM can then decide the next workflow step without reading untrusted email content.

The target: email as a typed event

Structured email for agents is not just text extraction. It is a typed event model with clear trust boundaries.

A good event should answer a few specific questions:

  • Which inbox received the email?
  • Which delivery event is being processed?
  • Which fields are provider-attested versus sender-claimed?
  • Which body representation was normalized?
  • Which artifacts were extracted?
  • Which values are safe for an agent to act on?
Representation Good for Weakness for agents
Raw RFC 5322 message Full forensic replay and debugging Too large, complex, and unsafe for direct model context
Rendered HTML email Human review Template-sensitive, can hide unsafe links or prompt text
Plain text body Lightweight parsing Still untrusted and often noisy
Structured email event Automation, QA, and LLM tools Requires a defined schema and ingestion pipeline
Minimal agent view Safe action selection Must be derived carefully from the full event

This shift is the difference between asking an agent to read a mailbox and giving it a reliable API result.

A line of holographic mail events moving through a programmable inbox, signed webhook verification, JSON normalization, artifact extraction, and a compact agent-safe response card, shown in a layered side view with dark reflective surfaces and drifting fog.

A reference pipeline for structured inbound email

The exact implementation depends on your stack, but the pattern is consistent: isolate the inbox, verify delivery, normalize the message, extract artifacts, and minimize what the agent can see.

Create an isolated inbox for the task

Do not start with a shared mailbox if the workflow needs determinism. Create a disposable inbox for the agent task, test run, signup attempt, or verification flow. Store both the email address and the inbox handle in your workflow state.

This matters because the address alone is not enough. The inbox identifier gives your code a stable resource to query, observe, and cleanly associate with one workflow. It also helps prevent an agent from accidentally selecting a stale email from a previous run.

With Mailhook, developers can create disposable inboxes via API, receive emails as structured JSON, and use shared domains or custom domain support depending on the workflow. For exact API semantics, keep the Mailhook llms.txt integration reference open while implementing.

Verify delivery before parsing

If email arrives through a webhook, verification should happen before JSON parsing or business logic. Webhook security is a separate layer from email authentication. DKIM or SPF may tell you something about the sender path, but they do not prove that the HTTP webhook request came from your inbox provider.

A production handler should verify the raw request body according to the provider contract, enforce timestamp freshness, reject replayed delivery IDs, and only then enqueue the event for processing. Mailhook supports signed payloads for this purpose, which is especially important when agents may take actions based on incoming messages.

Normalize MIME into stable fields

Email can contain multipart bodies, encoded headers, attachments, inline images, alternative text, and HTML. Your agent does not need to understand that complexity. The ingestion layer should normalize it into stable fields such as received time, recipients, subject, text body, HTML presence, attachment metadata, and provider or workflow IDs.

Prefer text/plain when it is available. HTML can be retained for debugging or controlled rendering, but it should not be the default input to an LLM. If your system must inspect HTML, sanitize it and avoid executing or fetching external resources.

Extract artifacts deterministically

Most agent workflows only need a small artifact from the email. That artifact might be an OTP, verification URL, reset link, invoice ID, ticket number, or alert severity. Extracting that value should be deterministic and testable.

For example, an OTP extractor should check message scope, sender expectations, code shape, and expiration context. A magic-link extractor should validate the host, scheme, path, and redirect behavior before exposing it. For URL safety, use allowlists and SSRF defenses, following guidance such as the OWASP SSRF Prevention Cheat Sheet.

Give the agent a minimized view

The final object handed to the LLM should be intentionally small. It should include the status, artifact type, validated value, source IDs, and any decision-relevant metadata. It should not include raw HTML, unrelated body text, tracking links, headers that the model does not need, or previous conversation content quoted inside the email.

A minimal response might look like this:

AgentEmailResult
  status: found
  inbox_id: ibx_123
  delivery_id: del_456
  artifact_type: otp
  artifact_value: 123456
  confidence: high
  source: verification_email

That is enough for an agent to continue the workflow without turning the email itself into a prompt.

A practical schema for agent-ready email

You do not need a huge schema to make inbound email useful. You need a small set of fields with stable meanings. The event below is provider-neutral and should be adapted to the exact contract of your email API.

AgentEmailEvent
  delivery_id: provider delivery identifier
  inbox_id: workflow inbox identifier
  received_at: provider receive timestamp
  recipient: address that received the message
  sender_claimed: from and reply-to values from the email
  subject: sender-claimed subject line
  text: normalized plain text body
  html_present: true or false
  artifacts: extracted OTPs, links, IDs, or labels
  raw_ref: pointer for debugging or replay, if stored
  verification: webhook signature and replay status from your consumer
Field Purpose Agent trust rule
delivery_id Dedupe a webhook or polling delivery Trust after webhook verification
inbox_id Route the email to the correct workflow Trust if it came from your inbox creation step
received_at Order and timeout logic Trust as provider-side metadata
sender_claimed Display and filtering signal Treat as untrusted input
subject Lightweight matching signal Treat as untrusted input
text Normalized readable content Treat as untrusted content, not instructions
artifacts Actionable derived values Act only after validation rules pass
raw_ref Debugging and replay Do not expose to the LLM by default
verification Consumer-side authenticity checks Use before processing the event

The important design choice is that the agent sees a result, not a mailbox. The parser and policy layer decide what is safe to expose.

Trust boundaries agents should respect

Not every field in a structured email event deserves the same level of trust. This is where many agent integrations go wrong. They parse the email into JSON, then treat every JSON value as equally authoritative.

Provider-attested fields are different from sender-claimed fields. An inbox ID assigned by your provider is not the same as a From header written by the sender. A delivery ID generated by your webhook system is not the same as a Message-ID header that may be missing, duplicated, or controlled by the sender.

Trust layer Examples How agents should use it
Provider-attested metadata inbox_id, delivery_id, received_at Routing, dedupe, timeouts, audit trails
Workflow state task_id, expected recipient, expected domain Strong correlation and authorization
Sender-claimed fields from, reply-to, subject, message-id Filtering hints, never sole authority
Derived artifacts OTP, validated URL, invoice ID Actionable only after extraction rules pass
Raw content HTML, quoted replies, attachments Keep out of agent context unless explicitly needed

This layered model lets you use email flexibly without giving untrusted content control over the agent.

Common artifact patterns

Different workflows need different extraction logic. A good structured data pipeline makes those differences explicit instead of hiding them inside a general LLM prompt.

Artifact Deterministic extraction rule Safe agent output
OTP code Match expected length, sender scope, inbox scope, and deadline Code, delivery ID, confidence
Magic link Extract from text, validate scheme and host allowlist Validated URL or opaque action token
Signup confirmation Match recipient, subject intent, and expected domain Confirmation received status
Ticket update Extract ticket ID and status label Ticket ID, status, source message
Security alert Classify severity with strict labels Severity, source, next allowed action
Attachment notice Capture filename and metadata Metadata only until scanning is complete

If the extraction cannot meet your confidence threshold, return a structured failure. Do not ask the model to guess. For example, return status: ambiguous with candidate count and source IDs, then let your orchestration layer decide whether to wait, retry, or escalate.

Reliability once email becomes data

Once inbound email is a data stream, treat it like any other event stream. Webhooks can be retried. Polling can see the same message twice. SMTP can deliver duplicates. Agents can restart midway through a task.

The fix is not to hope duplicates are rare. The fix is to design idempotency into every layer.

Failure mode Data design fix
Same webhook delivered twice Dedupe by delivery_id
Same email observed through webhook and polling Dedupe by message identity plus inbox_id
Same OTP extracted from duplicate messages Dedupe by artifact hash and task_id
Agent retries after partial success Store consume-once state for the artifact
Email arrives after timeout Use a drain window or mark as late arrival
Parallel tasks receive similar messages Use one inbox per task or attempt

This is also where real-time webhooks and polling work well together. Webhooks provide low-latency push delivery. Polling gives you a deterministic fallback when a worker restarts, a webhook endpoint is unavailable, or a test needs to recover state. Mailhook supports both real-time webhook notifications and a polling API, so you can choose the right pattern for each workflow.

For higher-volume systems, batch email processing can reduce overhead, but the same rules still apply. Each message and artifact should be deduped, validated, and traced back to the inbox and task that produced it.

Where Mailhook fits

Mailhook is built for teams that want inbound email to behave like an API primitive instead of a shared human mailbox. The platform provides programmable temporary inboxes via RESTful API access and delivers received emails as structured JSON for developers, LLM agents, QA automation, and signup verification flows.

In an agent architecture, Mailhook can sit between the outside email world and your tool layer:

create disposable inbox
use address in external workflow
receive JSON email through webhook or polling
verify signed payload
extract validated artifact
return minimal result to the agent

Relevant Mailhook capabilities include disposable inbox creation via API, structured JSON email output, real-time webhook notifications, polling for emails, instant shared domains, custom domain support, signed payloads for security, and batch email processing. You can also get started without a credit card.

For implementation details and the machine-readable integration contract, use Mailhook llms.txt. It is the best reference to pair with agent tool definitions and automation code.

Implementation checklist

Before you let an agent act on inbound email, review the pipeline as a contract rather than a prompt-engineering problem.

  • Create a dedicated inbox for each agent task, test run, or verification attempt.
  • Store inbox_id, task_id, expected recipient, and deadline in workflow state.
  • Verify webhook signatures before parsing or processing payloads.
  • Use polling as a fallback for recovery and deterministic waits.
  • Normalize email into structured JSON before model exposure.
  • Extract OTPs, links, and IDs with deterministic rules.
  • Validate URLs with scheme, host, path, and redirect constraints.
  • Give the agent only the minimal artifact and source IDs it needs.
  • Dedupe at delivery, message, and artifact levels.
  • Log stable IDs for debugging, but avoid logging secrets or full raw bodies.
  • Keep the exact Mailhook contract close by using llms.txt.

The goal is simple: agents should not browse inboxes. They should call narrow tools that return trusted, typed results.

Frequently Asked Questions

What does it mean to turn inbound email into structured data for agents? It means receiving email through an API inbox, normalizing the message into stable JSON fields, extracting validated artifacts, and returning a minimal tool result that an agent can safely use.

Should an LLM parse the full email body? Usually no. Use deterministic parsing first, then expose only the smallest safe result. Raw bodies, HTML, headers, and quoted replies should stay outside the model context unless there is a specific reason to include them.

Are webhooks or polling better for agent email workflows? Webhooks are best for low-latency delivery, while polling is useful as a fallback for recovery and deterministic waits. A webhook-first, polling-fallback design is usually the most reliable approach.

How do I protect agents from prompt injection in emails? Treat every inbound email as untrusted input. Do not expose raw HTML or full message bodies by default. Validate links, extract only typed artifacts, verify webhook signatures, and restrict the actions an agent can take from email-derived data.

Can structured inbound email work with a custom domain? Yes. Mailhook supports custom domain workflows as well as instant shared domains, which lets teams choose between fast setup and domain-level control for automation.

Where can I find the exact Mailhook API details for agents? Use the Mailhook llms.txt reference. It is designed to help developers and LLM-based tools understand the available integration primitives.

Build agent-ready email intake with Mailhook

If your agent needs to receive verification emails, OTPs, signup links, operational alerts, or other inbound messages, do not give it a mailbox. Give it structured data.

Mailhook lets you create disposable inboxes via API, receive emails as structured JSON, process messages through webhooks or polling, and verify signed payloads before your agent acts. Start with a shared domain for fast setup, use a custom domain when you need more control, and keep the llms.txt integration reference next to your implementation.

Related Articles