Source: https://cli.nylas.com/guides/microsoft-graph-mailbox-agent-best-practices

# 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](https://cli.nylas.com/authors/qasim-muhammad) Staff SRE

Updated June 9, 2026

> **TL;DR:** Grant the narrowest Graph scope the task needs (`Mail.Read` for triage, add `Mail.Send` only when it must reply), pace requests under Graph's 10,000-per-10-minute mailbox limit, and subscribe to change notifications instead of polling. Then put the real guardrail where an agent can't reach it — the one place containment actually has to live is below.

## 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](https://learn.microsoft.com/en-us/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`.

```bash
# 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](https://learn.microsoft.com/en-us/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.

```bash
# 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](https://learn.microsoft.com/en-us/graph/api/resources/subscription), 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.

```bash
# 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](https://cli.nylas.com/guides/agent-rules-and-policies) so the mailbox agent operates inside a fence it cannot argue its way out of.

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

## Next steps

- [Microsoft Graph Batch Requests Explained](https://cli.nylas.com/guides/graph-api-batch-requests-explained) — How Microsoft Graph $batch packs 20 requests into one call
- [Outlook OAuth for AI agents](https://cli.nylas.com/guides/outlook-oauth-ai-agents) — the Graph auth setup the CLI replaces, explained
- [Stop an AI agent going rogue](https://cli.nylas.com/guides/stop-ai-agent-going-rogue) — containment patterns that survive a prompt injection
- [Email prompt injection defense](https://cli.nylas.com/guides/email-prompt-injection-defense) — what an injected mailbox message can and can't make an agent do
- [Agent rules and policies](https://cli.nylas.com/guides/agent-rules-and-policies) — deterministic envelope rules that fire before the model
- [Command reference](https://cli.nylas.com/docs/commands) — every flag, subcommand, and example
- [Microsoft Graph throttling limits](https://learn.microsoft.com/en-us/graph/throttling-limits) — the per-mailbox request ceilings you pace against
