Guide

Microsoft Graph Mailbox Agent Best Practices

An AI agent that reads and writes an Outlook mailbox over Microsoft Graph needs three things right: least-privilege scopes, throttling-aware pacing, and change notifications instead of polling. This guide covers each, plus the containment a mailbox agent needs to stay safe.

Written by Qasim Muhammad Staff SRE

VerifiedCLI 3.1.17 · Outlook · last tested June 9, 2026

What scopes should an AI agent have on an Outlook mailbox?

An AI agent should hold the narrowest Microsoft Graph permission that completes its task — Mail.Read for a triage agent that only classifies, Mail.ReadWrite only if it moves or flags messages, and Mail.Send only when it actually replies. Each added scope widens the blast radius if the agent is compromised.

Microsoft draws a hard line between delegated permissions (the agent acts as a signed-in user) and application permissions (the agent acts as itself across many mailboxes), documented in the Microsoft Graph permissions reference. Application permissions over a whole tenant are the most dangerous grant a mailbox agent can hold. Confirm what a connected account actually has with nylas auth scopes.

# Show the OAuth scopes granted to the connected mailbox
nylas auth scopes

How do Microsoft Graph throttling limits affect a mailbox agent?

Graph throttles Outlook mailbox traffic at 10,000 requests per 10 minutes per app per mailbox, with no more than 4 concurrent requests against a single mailbox, per Microsoft's Graph throttling limits. An agent that loops over every message in a busy inbox hits this fast, and Graph answers with “429 Too Many Requests” plus a Retry-After header you must obey exactly.

The correct response is to sleep for the Retry-After value, then resume — never retry immediately, which extends the throttle window. Reading a page of headers and classifying locally beats fetching full bodies one at a time. A small pacing delay between bulk reads keeps an agent comfortably under the per-mailbox ceiling.

# Pace a bulk read so an agent stays under the per-mailbox limit
nylas email list --limit 50 --json | jq -r '.[].id' | while read id; do
  nylas email read "$id" --json | jq -r '.subject'
  sleep 0.3
done

How do I get mailbox updates without polling Graph?

Subscribe to change notifications instead of polling. Microsoft Graph pushes a notification to your endpoint when a message arrives or changes, which removes the constant list calls that burn your throttle budget. Per the Graph subscription resource, message subscriptions expire in under 3 days (a maximum of 4,230 minutes), so a long-running agent must renew them on a timer.

The CLI exposes the same push model across providers through webhooks, so you can build the agent's trigger once rather than per backend. The nylas webhook create command registers an endpoint for message.created events; your handler wakes the agent only when there's real work.

# Wake the agent on new mail instead of polling Graph on a loop
nylas webhook create \
  --url https://your-agent.example.com/inbound \
  --triggers message.created \
  --description "mailbox agent trigger"

Why is a read-and-send mailbox agent a lethal-trifecta risk?

A mailbox agent that reads incoming mail and can also send is exposed to the lethal trifecta — Simon Willison's term for the combination of private data, untrusted content, and external communication in one system. Email delivers all three: the inbox holds private data, every inbound message is untrusted content, and Mail.Send is an external channel an attacker would love to steer.

A prompt injection hidden in an email body will try to prompt its way past a rule you wrote into the system prompt. That's why containment lives outside the agent's decision loop: deterministic rules on the envelope — allowed recipient domains, blocked organizer domains, draft-don't-send by default — that fire before the model sees the message and that the model has no power to disable. A scope it doesn't hold is a scope it can't be tricked into using.

How does the Nylas CLI reduce Graph mailbox risk?

The CLI removes the Azure app registration entirely — no tenant admin consent dance, no app secret to leak — and gives the agent one command surface for Outlook that behaves like every other provider. Token refresh and the 429 class are handled below the command layer, so the agent never sees a stale-token failure mid-run.

Just as important, it keeps the guardrail where an agent can't reach it. You verify the granted scope with one command, gate sends behind deterministic rules, and default to drafts a human approves. Pair this with agent rules and policies so the mailbox agent operates inside a fence it cannot argue its way out of.

# Confirm auth and scope before the agent runs — fail closed if wrong
nylas auth status
nylas auth scopes

Next steps