An autonomous email agent that reads, decides, and sends has no human watching each action, so the policy you set is the only thing standing between a normal run and a runaway one. The agent might loop on a stuck thread, email a recipient it picked from an attacker’s forwarded message, or burn through your daily send quota in an hour. None of that is the model’s job to prevent, and you can’t trust the model to police itself.
This recipe sets enforceable policy on an Agent Account inbox at two layers: account-level Policies, Rules, and Lists that the platform enforces before mail reaches the provider, and a thin send wrapper in your own code for the checks the platform can’t see. Each agent also runs under its own identity, so its actions stay separate from your users and from every other agent.
How do I set rules or policies on an AI agent email account?
Section titled “How do I set rules or policies on an AI agent email account?”Set policy in two places. Attach a Policy, Rules, and Lists to the Agent Account’s workspace for limits and recipient blocks the platform enforces at SMTP, and wrap POST /v3/grants/{grant_id}/messages/send in your own code for context the platform can’t judge. Account-level rules fail closed, so a block rule rejects a send before it reaches the provider.
A Policy bundles limits and spam settings, a Rule matches mail and runs actions like block or assign_to_folder, and a List holds the domains or addresses a rule references. You don’t attach any of these to a single grant. Inbound rules apply to Agent Accounts through their workspace’s policy_id and rule_ids, so every account in the workspace inherits them. Outbound rules, including a send block, are evaluated application-wide from your sending application’s enabled rules rather than per-workspace. A single List holds up to 1000 items per add request, with each value capped at 500 characters, which covers most agent allowlists with room to spare.
Cap how many messages an agent can send
Section titled “Cap how many messages an agent can send”A runaway loop is the failure that hurts most, so cap send volume before anything else. Set limit_count_daily_email_sent on a Policy and the platform enforces it per Agent Account, refusing sends once the agent crosses the daily count. Without a Policy, an account runs at your billing plan’s maximum, which is usually far higher than any single agent should ever need.
The request below creates a Policy through POST /v3/policies with a daily send cap and tightened retention. Use it to give a prototype agent a low ceiling while a production sales agent runs on a separate, higher one. The limit_spam_retention_period must be shorter than limit_inbox_retention_period, so spam clears out ahead of the inbox.
curl --request POST \ --url "https://api.us.nylas.com/v3/policies" \ --header "Authorization: Bearer <NYLAS_API_KEY>" \ --header "Content-Type: application/json" \ --data '{ "name": "Prototype agent policy", "limits": { "limit_count_daily_email_sent": 200, "limit_inbox_retention_period": 90, "limit_spam_retention_period": 14 }, "spam_detection": { "spam_sensitivity": 1.0 } }'Attach the returned policy ID to a workspace with PATCH /v3/workspaces/{workspace_id}, and every account in that workspace picks up the 200-per-day ceiling. The send wrapper later in this recipe adds a tighter per-hour cap, because a daily limit alone can’t stop a burst that empties your quota in minutes.
Block who the agent can email
Section titled “Block who the agent can email”A send cap limits volume but not direction, so pair it with an outbound Rule that rejects sends to recipients the agent must never reach. An outbound block rule runs before the message leaves the platform and returns 403 with no sent copy stored. The recipient.* fields match against any recipient, including BCC and SMTP envelope addresses, so a hidden recipient can’t slip past.
The rule below blocks any send to a competitor or test domain through POST /v3/rules. Pair it with a List of domains so a non-engineer can update the blocked set without touching rule definitions: create a domain list, add up to 1000 items per request, then reference it with "operator": "in_list". For the inverse pattern, an application-side allowlist of approved recipients, see restrict AI agent email recipients.
curl --request POST \ --url "https://api.us.nylas.com/v3/rules" \ --header "Authorization: Bearer <NYLAS_API_KEY>" \ --header "Content-Type: application/json" \ --data '{ "name": "Block outbound to test and competitor domains", "trigger": "outbound", "match": { "conditions": [ { "field": "recipient.domain", "operator": "is", "value": "example.net" } ] }, "actions": [ { "type": "block" } ] }'Approve which actions the agent can take
Section titled “Approve which actions the agent can take”Decide the agent’s allowed actions in your own send wrapper, because the platform enforces limits and recipient blocks but can’t judge intent. Funnel every send through one function that runs your per-hour cap, an idempotency key, and a dry-run gate, then forwards an approved payload to POST /v3/grants/{grant_id}/messages/send. The agent gets exactly one way to send, and that path is the only place application policy lives.
The wrapper below threads three checks through a single call. The Idempotency-Key header accepts up to 256 characters, and the API caches each response for 1 hour per grant, so a retried call returns the original result instead of sending twice. A reused key with a different payload returns 409, which is your signal that something changed underneath a retry.
import uuid, time, requests
NYLAS = "https://api.us.nylas.com"_sends = {} # grant_id -> list of unix timestampsHOURLY_CAP = 50
def guarded_send(grant_id, message, api_key, dry_run=False): now = time.time() recent = [t for t in _sends.get(grant_id, []) if now - t < 3600] if len(recent) >= HOURLY_CAP: raise RuntimeError(f"hourly cap reached: {len(recent)} sends") if dry_run: return {"status": "dry_run", "would_send_to": message["to"]} resp = requests.post( f"{NYLAS}/v3/grants/{grant_id}/messages/send", headers={ "Authorization": f"Bearer {api_key}", "Idempotency-Key": str(uuid.uuid4()), "Content-Type": "application/json", }, json=message, ) resp.raise_for_status() recent.append(now) _sends[grant_id] = recent return resp.json()How can an autonomous agent send and receive email under its own identity?
Section titled “How can an autonomous agent send and receive email under its own identity?”Give the agent its own Agent Account, a dedicated grant with its own mailbox and address on a domain you control. The agent sends through POST /v3/grants/{grant_id}/messages/send and receives through a message.created webhook, all under that one grant. Its sent and received mail never mixes with a user’s mailbox or with another agent’s, which keeps audit trails clean.
A separate identity matters for accountability. When every send carries the agent’s own from address, you can attribute any message to exactly one agent, and you can revoke that one grant without touching anyone else. Subscribe a message.created webhook through POST /v3/webhooks to drive the receive side, and the agent reacts to replies in its own thread. Each webhook payload caps at 1 MB; bodies over that arrive as message.created.truncated with the body omitted, so fetch the full message before acting. For the full setup, see give your agent its own email.
Audit every send the agent makes
Section titled “Audit every send the agent makes”Audit at both layers, because an autonomous agent acts while nobody watches and you need to reconstruct what happened after the fact. The platform records a rule-evaluation entry every time it judges an inbound or outbound message for an Agent Account. List them through GET /v3/grants/{grant_id}/rule-evaluations, most recent first, to answer “why did this send get blocked?” in under 1 minute instead of digging through provider logs.
Each evaluation record carries evaluation_stage (outbound_send for a send), the evaluation_input of normalized recipient data considered, the matched_rule_ids, and applied_actions with blocked: true when the send was rejected. Records return in reverse chronological order, so the most recent decision sits at the top of the first page. Layer your own append-only log on top: write one record per decision with the inbound message ID, the recipient, whether each application check passed, the idempotency key, and the outcome, including the sends your wrapper declined.
Account policy vs application send wrapper
Section titled “Account policy vs application send wrapper”Account-level Policies and Rules and your application send wrapper cover different gaps, and a serious agent uses both. The table compares where each control runs. The unified Nylas account policy column shows what the platform enforces before mail reaches the provider, no application code required.
| Control | Application send wrapper | Unified Nylas account policy |
|---|---|---|
| Daily send quota | Manual counter per grant in your store | limit_count_daily_email_sent on a Policy, enforced platform-side |
| Block recipient domains | Allowlist check before the send call | Outbound block rule, rejects with 403 before the provider |
| Per-hour burst cap | Rolling counter in the wrapper | Not available at the account level |
| Dry-run preview | Wrapper returns recipients, sends nothing | Not available |
| Audit trail | Your append-only log of every decision | GET /v3/grants/{grant_id}/rule-evaluations, per grant |
| Spam and retention | Out of scope | spam_sensitivity and retention fields on a Policy |
One honest tradeoff: if your agent legitimately emails arbitrary external recipients, for example an outreach tool that contacts any prospect, a static recipient block will reject valid sends and stall the workflow. In that case drop the outbound block rule, lean on the daily and per-hour caps, and add a human-approval step for first contact with a new domain instead.
What’s next
Section titled “What’s next”- Restrict AI agent email recipients for the application-side allowlist and dry-run gate
- Handle email replies in an agent loop for anchoring reply recipients to the source thread
- Build an autonomous email agent for the full guardrail set: rate cap, kill switch, and audit log
- Policies, Rules, and Lists for the complete account-level policy reference