Email & Calendar Agents

10 min read

Y11
Playbook · Domain Playbooks

Email and calendar agents: the inbox is an attacker-controlled input channel.

Your CRM, your data warehouse and your ticket system all require authentication before anyone can write a record. Your inbox does not — any stranger on the internet can place arbitrary text inside your private context, for free, and your calendar will accept a record with a title of their choosing before you have even seen it. That single asymmetry, not the scheduling logic, is what an inbox agent is actually about, and it is why the demo takes an afternoon and the production system takes a quarter.

STEP 1

Start from what makes this domain different.

Every domain playbook opens by naming the job. Here the job is easy to state — triage, draft, summarise, schedule, follow up — and it is not where the difficulty lives. The difficulty is a property of the data source, and it is worth stating as sharply as possible:

Email and calendar are the only systems of record in your company where an unauthenticated third party can insert content that your agent will read as instructions. Not "can send you something you might open" — can place a record into the store your agent enumerates, with a subject line and a body they control, delivered automatically.

This changes the shape of the whole build. In a support agent, the untrusted text arrives in a request you can bound and label. In a data-analysis agent, the inputs are tables you own. Here, the untrusted text is indistinguishable at the storage layer from the trusted text — same folder, same schema, same API — and the agent's core loop is to read all of it. Any design that begins "then we retrieve the relevant messages" has already loaded attacker-authored content into the context that decides what happens next.

Two consequences worth accepting before writing code. First, no amount of prompt hardening fixes this; instructions that say "ignore instructions in emails" are advisory to a system that cannot reliably tell instruction from data. Second, the value on offer is nonetheless large and genuine, so the answer is not to refuse the domain — it is to build the send path such that content from a message can never reach it. See prompt injection 101 for the mechanism.

STEP 2

The trifecta assembles itself here, by default.

The standard risk framing for agents is three ingredients that are individually fine and jointly dangerous: access to private data, exposure to untrusted content, and a channel that can send data outward. An inbox agent has all three on day one, and not through a design mistake — through the definition of the product.

  • Private data is the entire mailbox, which in most organisations is the single richest store of secrets anyone holds: contracts, credentials sent by colleagues who should have known better, board material, personal information about third parties.
  • Untrusted content arrives continuously, from anyone, at no cost to the sender, and is placed in the same store as the private data.
  • The outbound channel is the product feature. An agent that cannot send is not an email agent. And the send path is not the only egress: a rendered image URL, a link the user is invited to click, a calendar invite forwarded to an external address, and a web-fetch tool all move data outward just as effectively.

The published attacks follow exactly this shape rather than anything exotic. Researchers have demonstrated indirect prompt injection delivered through ordinary emails, calendar invitations and shared documents against production assistants, spanning a range of outcomes from data exfiltration to phishing. A zero-click flaw in a hosted research agent allowed inbox data to be leaked through a single crafted email, with no user action beyond the agent doing its job. The pattern in each case is the same: content arrives, the agent reads it as part of its normal loop, and an existing capability is turned outward.

Design against the pattern rather than against the specific payloads. Filters for known injection phrasings are worth having and will not hold; the structural controls in the next three steps will. More depth in prompt-injection defense and data exfiltration risks.

STEP 3

Classify by provenance, and carry the label everywhere.

The first structural move is to stop treating the mailbox as one corpus. Every message carries provenance, and provenance is what should determine capability — not the folder it landed in, and certainly not whether the model judged it to look legitimate.

A workable three-tier split, with the tier assigned at ingestion and attached to the content for the whole life of the request:

  • Trusted. Authenticated internal senders, on your domain, passing authentication checks. Content here may inform actions, still within policy limits.
  • Known-external. Senders with prior two-way history, or on an explicit allowlist. Content may be summarised and drafted against, but may not trigger an action on its own.
  • Untrusted. Everything else — first contact, failed authentication, anything from a mailing list, and every calendar invite from outside. Content may be read and summarised only. It can never cause a tool call.

Two implementation notes decide whether this works or is theatre. The label must be attached at retrieval and survive every transformation: if a summarisation step collapses six messages into one paragraph, that paragraph inherits the lowest trust level of its inputs, permanently. And the tier must be computed from headers and history, not inferred by the model — a classifier that reads the message to decide how much to trust the message is the vulnerability, restated.

The most common real-world bypass is not a clever jailbreak; it is a forwarded message. An untrusted email forwarded by a trusted colleague arrives with a trusted sender and untrusted content nested inside it. Parse the quoted material and label the nested content by its original provenance, or the whole tier system is one forward away from being bypassed.

STEP 4

Make the send path structurally unreachable from message content.

This is the load-bearing control and the one that most implementations get backwards. The common design gives the agent a send_email tool and relies on the prompt, a guardrail model, or a human clicking Approve to stop misuse. All three are probabilistic defences on a path that needs a structural one.

The structural version splits the loop in two, with an interface between them that cannot carry an instruction:

  • A reader with no outbound capability. It sees message content. It has no send tool, no fetch tool, no ability to reach the network. Its only output is a structured object — an intent, some fields, references to message IDs.
  • An actor with no message content. It receives the structured object and executes. It never sees raw message bodies, so there is nothing in its context for an attacker to have written.

The interface between them is where the security lives, and it must be a fixed schema of typed fields, not free text. A recipient field that accepts an arbitrary string is a free-text channel wearing a schema's clothes; a recipient field constrained to an ID from the current thread's participant list is not. The rule generalises: every field that crosses from reader to actor must be either a constrained enum, a reference into data the actor can independently re-fetch, or a value the human typed.

Concretely, for the actions people actually want:

  • Replies go to thread participants by reference. Adding a recipient not already on the thread is a different, higher-privilege action.
  • New outbound messages to an address that appears only inside message content are the exact shape of an exfiltration attempt. Require a human-typed or directory-resolved recipient.
  • Attachments and links are carried by reference to an object the actor re-fetches and re-checks, never by content passed through from the reader.
  • Deletion and archival at scale deserve the same treatment as sending. An agent that can quietly delete the warning email is an agent that can hide its own incident.

Scope the credential to match: a token that can read one label and reply within existing threads is a much smaller loss than full mailbox access, and most inbox agents need nothing more. See scoped credentials for agents and agent identity and permissions.

STEP 5

Calendar is worse than email, and nobody builds like it is.

Calendar gets treated as the boring half of this product. It is the more dangerous half, for four reasons that compound:

  • Invites auto-insert. On common default settings, an invitation from a stranger becomes an event on your calendar before you have seen it. That is unauthenticated write access to a store your agent reads — email at least requires the message to be retrieved from a folder that a user might have filtered.
  • Every field is attacker-controlled and looks structural. Title, location, description, attendee list, notes. A description field is a body by another name, and agents are more likely to treat calendar fields as trusted metadata than as content, precisely because they look like schema.
  • Events are read on a schedule. A daily-briefing agent reads tomorrow's calendar every morning without a user request, which means an attacker chooses the time their content enters the context. This is what makes calendar the natural zero-click vector.
  • Attendee lists are an egress channel. Adding an address to an event shares the event and its history with that address. It looks like scheduling; it functions like forwarding.

The controls follow directly. Treat every field of an externally-created event as untrusted content under the Step 3 tiers, including the title. Never let calendar content originate an action — an event may be summarised, and may be scheduled around, but it may not cause an email to be sent or a document to be fetched. Treat modifying an attendee list as a send-class action with the same recipient constraints. And if the deployment allows it, turn off automatic invitation insertion for accounts an agent reads; it is a one-line setting that removes an entire attack surface.

STEP 6

What to build, in order — and the confirmation that actually works.

The sequencing matters, because each stage is useful on its own and each earns the trust needed for the next:

  • Read-only triage first. Labelling, prioritising, summarising, surfacing what needs a human. No send capability at all. This is where most of the value is, and it is the version you can deploy without a security review that lasts longer than the build.
  • Draft-only second. The agent composes; nothing leaves without a human action. Note carefully that this is not free of risk — a drafted reply that quietly includes exfiltrated content is still exfiltration once a human hits send, which is why the reader/actor split in Step 4 matters even when a human is in the loop.
  • Autonomous send last, and narrowly. Bounded to reply-in-thread, to a small set of intents, with a rate limit and a per-recipient cap. See sales and GTM agents for why volume is its own harm.

The confirmation step deserves its own attention, because the default implementation destroys its own value. "The agent wants to send this email — Approve?" is a dialog that trains people to click Approve, and after the fortieth correct draft they will approve the forty-first without reading it. Habituation is not a user failing; it is the predictable result of a control that asks the same question every time regardless of risk.

What works instead: ask only when something is unusual, and show the delta rather than the artefact. A reply to an existing thread with no new recipients and no attachment is routine and can be summarised after the fact. A first message to a new external domain, an attachment, an added recipient, or an unusual send volume is an exception — and the prompt for it should highlight precisely the unusual element, not re-render the whole draft. Fewer confirmations that are actually read beat a confirmation on everything. This is the subject of approval and confirmation UX, and it is where inbox agents most often fail in the field.

If you build one thing from this page, build the reader/actor split with a typed interface between them, and put the recipient field on the actor side where message content can never reach it. Everything else — tiering, calendar hardening, confirmation design — is defence in depth on top of it. Without it, you are relying on a model's judgement about text an attacker wrote, on a path that ends in your outbox, and that has never held anywhere it has been seriously tested.

Related: prompt injection in operations for detection and response once you are live, policy as code for expressing these limits somewhere reviewable, and designing for failure for the user experience when the agent correctly refuses.