惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
The Exploit Database - CXSecurity.com
N
News and Events Feed by Topic
Latest news
Latest news
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
CXSECURITY Database RSS Feed - CXSecurity.com
IT之家
IT之家
V
V2EX
WordPress大学
WordPress大学
Apple Machine Learning Research
Apple Machine Learning Research
Cisco Talos Blog
Cisco Talos Blog
K
Kaspersky official blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
SegmentFault 最新的问题
小众软件
小众软件
A
Arctic Wolf
酷 壳 – CoolShell
酷 壳 – CoolShell
腾讯CDC
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
G
GRAHAM CLULEY
罗磊的独立博客
T
Tor Project blog
C
Cisco Blogs
美团技术团队
博客园 - Franky
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
T
Threat Research - Cisco Blogs
Cyberwarzone
Cyberwarzone
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
有赞技术团队
有赞技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Security Latest
Security Latest
博客园 - 司徒正美
Hugging Face - Blog
Hugging Face - Blog
Spread Privacy
Spread Privacy
J
Java Code Geeks
C
CERT Recently Published Vulnerability Notes
大猫的无限游戏
大猫的无限游戏
S
Securelist
The Cloudflare Blog
博客园 - 叶小钗
D
Darknet – Hacking Tools, Hacker News & Cyber Security
阮一峰的网络日志
阮一峰的网络日志
雷峰网
雷峰网
Project Zero
Project Zero

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Filter what your AI email agent sends and receives
Qasim · 2026-06-23 · via DEV Community

An AI agent with its own mailbox reacts to whatever lands in it. That's the point, until a spam blast, a mailer-daemon loop, or an auto-reply triggers the agent into answering noise. The same goes the other way: an agent composing mail on its own can address the wrong person, leak to a test domain that slipped into production, or email a competitor because nobody told it not to. A human would catch these. An agent needs guardrails encoded somewhere it can't skip.

Agent Accounts ship three admin resources for exactly this: Policies bundle limits and spam settings, Rules match mail on the way in or out and run actions like block or assign_to_folder, and Lists are reusable collections of domains or addresses that rules reference. This post covers all three from two angles: the HTTP API for your backend, and the Nylas CLI for inspecting and managing policies and rules from the terminal. Lists, workspaces, and the rule-evaluations audit log are API-only for now. I work on the CLI, so the terminal commands below are the ones I reach for.

How Policies, Rules, and Lists fit together

The three resources form a chain, and a workspace ties it to your accounts. A List holds values like domains or addresses. A Rule references lists through the in_list operator and describes conditions and actions. A Policy bundles limits and spam settings. A workspace carries one policy_id plus an array of rule_ids, and every Agent Account in that workspace inherits both.

What matters here: you don't attach a policy or rule to an individual grant. You set policy_id and rule_ids on a workspace, and they apply to every account in it. Each application has a default workspace that holds any account you haven't placed elsewhere, so configuring that one workspace covers all your unassigned accounts at once. All three resources are application-scoped — they carry no grant ID in the path, and your API key identifies the application.

Resource What it owns How it's referenced
List A typed collection of domains, TLDs, or email addresses By ID, from a rule condition when operator is in_list
Rule A trigger (inbound or outbound), match conditions, and actions By ID, in a workspace's rule_ids array
Policy Limits and spam detection settings By ID, in a workspace's policy_id
Workspace The policy_id and rule_ids that govern its accounts By ID, in a grant's workspace_id

They're optional. Without a workspace policy, an account runs at your billing plan's maximum limits; without workspace rules, inbound mail is delivered to the inbox unfiltered. Reach for these only when you want stricter behavior or filtering.

Inspect what's already configured from the CLI

Before changing anything, see what exists. The Nylas CLI lists your policies and rules so you can audit the current setup without opening the Dashboard. This is usually my first step when I pick up an application I didn't configure myself.

# List policies and rules on the application
nylas agent policy list
nylas agent rule list

# Fetch a single policy or rule by ID
nylas agent policy get <policy-id>
nylas agent rule get <rule-id>

The same listing is available over the API at GET /v3/policies and GET /v3/rules. Both surfaces return the application's resources, since policies and rules aren't grant-scoped. Once you know what's there, you can create new ones or update the workspace assignments.

Set limits and spam detection with a Policy

A policy is the configuration you reuse across many accounts. It holds a limits object — attachment size and count, allowed MIME types, total message size, per-account storage, daily send quota, and inbox and spam retention — and a spam_detection object with DNSBL checking, header anomaly detection, and a spam_sensitivity dial from 0.1 to 5.0 where higher is more aggressive. Every limit is optional and defaults to your plan's maximum; requesting a value above the plan maximum returns an error.

These are the limit fields you set most often, each enforced per Agent Account:

Field What it controls
limit_count_daily_message_received Messages the account can receive per day
limit_attachment_size_limit Size of a single inbound attachment, in bytes
limit_attachment_count_limit Attachments allowed on one inbound message
limit_count_daily_email_sent Messages the account can send per day
limit_storage_total Total stored bytes per account
limit_inbox_retention_period Days a message stays in the inbox before deletion
limit_spam_retention_period Days a message stays in spam before deletion

Create a policy over the API with POST /v3/policies. The body below caps attachments at 25 MB, keeps inbox mail for a year and spam for 30 days, and turns spam detection up slightly to a sensitivity of 1.5:

curl --request POST \
  --url "https://api.us.nylas.com/v3/policies" \
  --header "Authorization: Bearer <NYLAS_API_KEY>" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "Standard Agent Account Policy",
    "limits": {
      "limit_attachment_size_limit": 26214400,
      "limit_inbox_retention_period": 365,
      "limit_spam_retention_period": 30
    },
    "spam_detection": {
      "use_list_dnsbl": true,
      "use_header_anomaly_detection": true,
      "spam_sensitivity": 1.5
    }
  }'

The CLI creates the same policy from the terminal. nylas agent policy create takes a --name and the configuration as inline JSON with --data, or from a file with --data-file — handy for keeping policy definitions in version control:

nylas agent policy create \
  --name "Standard Agent Account Policy" \
  --data-file ./standard-policy.json

A policy does nothing until a workspace references it. Attach it by setting policy_id on a workspace with PATCH /v3/workspaces/{workspace_id}, and every account in that workspace picks up its limits and spam settings. Set the spam retention shorter than the inbox retention, since spam should clear out ahead of real mail.

Block inbound spam before the agent sees it

A rule decides what to do with a message, either on the way in or on the way out. An inbound rule matches on sender fields and runs actions when it hits. The most useful one for an agent is block: it rejects the message at the SMTP layer, so the message never lands in the mailbox and your application never sees it. Inbound rules can match from.address, from.domain, and from.tld with the operators is, is_not, contains, and in_list.

This rule blocks every message from a known bad domain. Rules run in priority order from 0 to 1000, lowest first, defaulting to 10, and a block action is terminal — it can't be combined with other actions:

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 spam-domain.com",
    "priority": 1,
    "trigger": "inbound",
    "match": {
      "conditions": [
        { "field": "from.domain", "operator": "is", "value": "spam-domain.com" }
      ]
    },
    "actions": [{ "type": "block" }]
  }'

From the CLI, nylas agent rule create builds the same rule. You can pass the whole definition with --data-file, or assemble it inline with --trigger, --condition, and --action flags:

nylas agent rule create \
  --name "Block spam-domain.com" \
  --data-file ./block-rule.json

Block isn't the only action. To route instead of reject, pair assign_to_folder with mark_as_read to move newsletters into a reading folder and mark them read in one rule. The full action set is block, mark_as_spam, assign_to_folder, mark_as_read, mark_as_starred, archive, and trash. A rule only runs once a workspace lists its ID in rule_ids; one array carries both inbound and outbound rules, and Nylas filters by trigger at evaluation time.

Stop sends to the wrong recipients with outbound rules

Outbound rules are where an agent gets data-loss prevention. They run when the account issues a send, before the message reaches the email provider, so a block action short-circuits delivery entirely — the API returns 403 and no sent copy is stored. Outbound rules can match from.*, plus recipient.address, recipient.domain, recipient.tld, and outbound.type.

The recipient.* fields match against any recipient, including BCC and SMTP envelope recipients, which is what makes them useful for DLP — a hidden BCC to a blocked domain still trips the rule. This one blocks any send to a specific domain, the kind of hard stop you'd use for a competitor or a test domain:

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 example.net",
    "trigger": "outbound",
    "match": {
      "conditions": [
        { "field": "recipient.domain", "operator": "is", "value": "example.net" }
      ]
    },
    "actions": [{ "type": "block" }]
  }'

The outbound.type field narrows a rule to replies or brand-new messages without looking at recipients at all. It takes two values: reply when the send includes reply_to_message_id or the raw MIME carries In-Reply-To or References, and compose for everything else. It supports only is and is_not. A rule with outbound.type is reply lets you, say, auto-star every reply the agent sends. Non-blocking outbound actions like archive and mark_as_starred apply to the stored sent copy after the send succeeds — they don't change what the recipient receives.

One behavior worth designing for: rule evaluation fails closed. If a block rule can't be evaluated because of a transient error, like a list lookup failing during in_list matching, Nylas blocks rather than letting the message through. Inbound, that surfaces as a 451 tempfail so the sending server retries; on an API send it returns 503 instead of 403, so your application can retry too.

Maintain dynamic allow and block lists

When the set of blocked domains changes over time, you don't want to edit rules every time. A List is a typed collection of values a rule matches against through in_list, so you update the list and every rule referencing it picks up the change immediately. Each list has a fixed type set at creation: domain for domain names, tld for top-level domains, or address for full email addresses.

Lists have no CLI command, so you manage them over the API. Create a list, then add up to 1000 items per request. Values are lowercased and trimmed on write and validated against the list's type, so a domain list rejects full email addresses:

# Create the list
curl --request POST \
  --url "https://api.us.nylas.com/v3/lists" \
  --header "Authorization: Bearer <NYLAS_API_KEY>" \
  --header "Content-Type: application/json" \
  --data '{ "name": "Blocked domains", "type": "domain" }'

# Add items to it
curl --request POST \
  --url "https://api.us.nylas.com/v3/lists/<LIST_ID>/items" \
  --header "Authorization: Bearer <NYLAS_API_KEY>" \
  --header "Content-Type: application/json" \
  --data '{ "items": ["spam-domain.com", "another-bad-domain.net"] }'

To use the list, set a rule condition's operator to in_list and pass one or more list IDs as the value. A single in_list condition can reference up to 10 lists. Rules cap at 50 conditions and 20 actions each, with up to 500 characters per condition value, so a list is the right tool once a block set grows past a handful of inline values. Lists let a non-engineer update who's allowed or blocked without touching rule definitions or redeploying anything.

Audit which rules ran

When a message gets blocked, routed, or marked and you need to know why, the rule-evaluations endpoint is the fastest answer. Every time the engine evaluates an inbound message, an SMTP envelope, or an outbound send for an account, Nylas records an audit entry. List them, most recent first, with GET /v3/grants/{grant_id}/rule-evaluations:

curl --request GET \
  --url "https://api.us.nylas.com/v3/grants/<GRANT_ID>/rule-evaluations?limit=50" \
  --header "Authorization: Bearer <NYLAS_API_KEY>"

Each record names the evaluation stage — smtp_rcpt when a message was rejected before acceptance, inbox_processing after acceptance, or outbound_send at send time — the normalized sender and recipient data considered, the IDs of any matched rules, and the actions applied. When a block came from a fail-closed evaluation error rather than a genuine match, the record carries blocked_by_evaluation_error: true, so you can tell an infrastructure hiccup apart from a real rule hit. Cross-reference matched_rule_ids with nylas agent rule get to see the conditions behind each match.

Things to keep in mind

A few practices keep this system predictable as it grows. None are complicated, but each one prevents a class of surprise.

  • Order rules by specificity. Lower priority runs first and the first block is terminal, so put narrow rules (is, in_list against a small list) ahead of broad ones (contains).
  • Pick the right trigger. Inbound rules only see received mail; outbound rules only see sends. They're isolated on purpose, so don't try to filter sends with an inbound rule.
  • Handle 403 from sends. An outbound block returns 403 with no sent copy stored. Treat it like any delivery failure — no retry path will deliver it.
  • Prefer lists over inline values when the set will grow. One list feeds many rules and updates without touching rule definitions.
  • Start spam sensitivity at 1.0 and tune from there: up if spam slips through, down if real mail gets marked.
  • Use separate workspaces per agent archetype. A sales-outreach agent and a support-triage agent have different send limits and spam tolerances — give each its own workspace and policy rather than one catch-all.

Wrapping up

Policies, Rules, and Lists give an agent the judgment it doesn't have on its own: what to drop on the way in, what to refuse on the way out, and how much it's allowed to send and store. The model is small once it clicks — lists feed rules, a policy bundles limits, and a workspace carries both to every account inside it. Build the pieces over the API or the CLI, attach them to a workspace, and the audit trail tells you exactly what fired.

Where to go next: