ساخت یک قانون
شرطها در یک سو، اقدامها در سوی دیگر. مگر خلافش را بگویید، فعال است.
فراخوانی واقعی را با کلید خودتان روی فضای کاری شما اجرا میکند.
POST /rules
شرطها در یک سو، اقدامها در سوی دیگر. مگر خلافش را بگویید، فعال است.
نمونه
نیازمند rules:write است. 201 برمیگرداند. position پذیرفته نمیشود. قانون تازه به انتهای فهرست افزوده میشود، و جابهجاکردنش POST /rules/reorder است.
curl -X POST "$OE/rules" -H "$AUTH" -H "Content-Type: application/json" \ -d '{ "name": "Receipts to their own label", "match": "all", "conditions": [ { "field": "from_domain", "op": "matches", "value": "*.stripe.com" }, { "field": "subject", "op": "contains", "value": "receipt" } ], "actions": [ { "type": "label", "value": "USER_RECEIPTS" }, { "type": "archive" } ], "stopProcessing": true }'{ "object": "rule", "id": "rul_7f3a1c94e05d3862c1f0a44b", "name": "Receipts to their own label", "description": null, "enabled": true, "position": 3, "match": "all", "conditions": [ { "field": "from_domain", "op": "matches", "value": "*.stripe.com", "negate": false }, { "field": "subject", "op": "contains", "value": "receipt", "negate": false } ], "actions": [ { "type": "label", "value": "USER_RECEIPTS" }, { "type": "archive" } ], "stopProcessing": true, "lastMatchedAt": null, "matchCount": 0, "createdAt": "2026-08-30T10:41:02.000Z", "updatedAt": "2026-08-30T10:41:02.000Z"}قانونی که اینجا ساخته شود روشن است و از پیام بعدی شروع به عمل میکند. برای فراخوانیای که کسی عمداً کرده پیشفرض درست همین است، و درست برعکس ابزار createRule در MCP است که همان قانون را غیرفعال مینویسد، چون مدلی که تصمیم میگیرد نامهای را بایگانی کند نباید پیش از آنکه شخصی قانون را بخواند مشغول بایگانی باشد.
name تکراری روی همان connection یعنی rule_name_taken، یک 409. نامها همان چیزیاند که یک قانون را در لاگ اجرا و در صفحهٔ تنظیمات میشناسانند، پس دو قانون به نام «Newsletters» گزارشی است که کسی نمیتواند بخواند.
صد و یکمین قانون rule_limit_reached است، یک 422. این سقف محافظی در برابر اسکریپتی در حلقه است نه مرزی حسابداری، و قفل هم نیست. دو ساخت همزمان در ۹۹ میتوانند هر دو موفق شوند.
یک شرط چه میتواند بپرسد
یک شرط { field, op, value } است، با یک header اختیاری که میگوید کدام هدر خوانده شود و یک negate اختیاری. value روی سیم همیشه string است. فیلدهای عددی پس از Number(value) به صورت عدد مقایسه میشوند، و آن دو فیلد بولین رشتههای تحتاللفظی "true" و "false" را میگیرند، چون یک فیلد با یک نوع اسکیمایی است که تولیدکنندهٔ OpenAPI میتواند توصیفش کند و اجتماع سه نوع نیست.
| فیلد | چه میخواند | عملگرها |
|---|---|---|
| `from` | هدر From:، نرمالشده به همان شکلی که فهرست مسدودی نرمال میکند. | text |
| `from_domain` | دامنهٔ From: و والدهایش، تا دو برچسب: پیامی از mail.corp.example.com با corp.example.com و example.com هم تطبیق مییابد، و با com با هیچچیز تطبیق نمییابد. | text |
| `envelope_from` | همان MAIL FROM در SMTP. روی هر فهرست پستی با from فرق دارد، و تنها هویتی است که میتوان یک reject را در برابرش نوشت. | text |
| `to`, `cc`, `bcc` | هر یک از نشانیهای آن هدر. | text |
| `recipient` | هر نشانی در to، cc یا bcc: سرنام هر سه. | text |
| `reply_to` | هدر Reply-To. | text |
| `delivered_to` | نشانی متعارفی که این نسخه به آن تحویل شده، با برچسب مثبت حذفشده و حروف کوچکشده، که همان شیوهٔ تطبیق یک نام مستعار catch-all است. | text |
| `subject` | خط موضوع، همانطور که رسیده است. | text |
| `body` | بخش متنی، یا HTML تقلیلیافته به متن. سقف دارد، پس بدنهای 20 MB کامل پویش نمیشود. | text |
| `header` | هر هدری، که در فیلد header خود شرط نام برده میشود. آنجا الزامی است و پیش از مقایسه حروفش کوچک میشود. | text |
| `list_id` | هدر List-Id: دستگیرهای که یک فهرست پستی خود را با آن میشناساند. | text |
| `attachment_name` | نام فایل هر پیوستی. | text |
| `attachment_type` | نوع MIME هر پیوستی، مثلاً application/pdf. | text |
| `has_attachment` | اینکه اصلاً پیوستی هست یا نه. | equals "true" / "false" |
| `spam` | حکم هرزنامهای که مسیر تحویل پیش از اجرای قانونهای شما به آن رسیده است. | equals "true" / "false" |
| `attachment_size` | اندازهٔ یک پیوست به بایت. مقایسه وقتی تطبیق مییابد که هر یک از پیوستها آن را برآورده کند. | gt, lt, equals |
| `message_size` | کل پیام روی سیم، به بایت. | gt, lt, equals |
| `hour` | ساعت رسیدن، ۰ تا ۲۳، به وقت UTC. | gt, lt, equals |
| `weekday` | روز رسیدن، ۰ تا ۶، یکشنبه ۰ است، به وقت UTC. | gt, lt, equals |
| عملگر | چه میکند |
|---|---|
| `matches` | یک glob، و فقط glob: * برای هر رشتهای از نویسهها، ? برای یکی. عبارت باقاعده در کار نیست. الگویی که از کلاینت API میآید روی مسیر تحویل اجرا میشود، و الگویی با بازگشت فاجعهبار در آنجا یعنی صندوق پستیای که دیگر دریافت نمیکند. |
| `contains` | زیررشته، بدون حساسیت به بزرگی و کوچکی حروف. |
| `equals` | کل مقدار، بدون حساسیت به بزرگی و کوچکی حروف. روی فیلد عددی، برابری عددی. |
| `starts_with` | پیشوند، بدون حساسیت به بزرگی و کوچکی حروف. |
| `ends_with` | پسوند، بدون حساسیت به بزرگی و کوچکی حروف. |
| `gt`, `lt` | عددی، تنها روی آن چهار فیلد عددی. فیلد متنی با gt هرگز تطبیق نمییابد. |
الگوی matches باید دستکم دو نویسهٔ حرفیعددی از خودش داشته باشد، همان حداقلی که فهرست مسدودی اعمال میکند. یک * تنها هنگام نوشتن رد میشود نه اینکه پذیرفته شود و بعد بیصدا با هر پیامی که تا ابد خواهد رسید تطبیق یابد، که یک قطعی است نه یک قانون.
شرطی که موتور نمیتواند به آن پاسخ دهد (فیلدی ناشناخته از کلاینتی تازهتر، الگویی که کامپایل نمیشود، contains "") پرسشی تلقی میشود که هرگز پرسیده نشده، نه نادرست، و negate آن را وارونه نمیکند. این تمایز بار زیادی به دوش دارد: شرط خرابِ نفیشده اگر نادرست تلقی میشد، قانونش روی هر پیام صندوق پستی عمل میکرد. equals "" محترم شمرده میشود، چون «خط موضوع خالی است» پرسش واقعی است.
یک قانون چه میتواند بکند
| اقدام | `value` | چه رخ میدهد |
|---|---|---|
| `label` | شناسهٔ یک برچسب | برچسب را میافزاید. شناسههای USER_… از GET /labels میآیند. |
| `remove_label` | شناسهٔ یک برچسب | برش میدارد. نامبردن یک برچسب در هر دو، پیش از بایگانیشدن پیام حل میشود نه اینکه به هر کدام که آخر اجرا شد واگذار شود. |
| `archive` | ندارد | آن را از صندوق ورودی بیرون میبرد. |
| `mark_read` | ندارد | UNREAD را برمیدارد. |
| `star` | ندارد | STARRED را میافزاید. |
| `spam` | ندارد | آن را زیر Spam بایگانی میکند. |
| `trash` | ندارد | آن را زیر Trash بایگانی میکند و برچسبهایی را که پیام دورانداخته نگه نمیدارد پاک میکند. |
| `forward` | یک نشانی | یک نسخه را جلو میفرستد. پیش از استفاده یادداشت پایین را بخوانید. |
| `reply` | شناسه یا slug یک قالب | با یک قالب منتشرشده پاسخ خودکار میدهد، مشروط به محافظ حلقهٔ پایین. |
| `block_sender` | ندارد | فرستنده را به فهرست مسدودی میافزاید، پس پیام بعدی همان دم در رد میشود. |
| `reject` | ندارد | پیام را هنگام SMTP با 550 5.7.1 Message refused by the recipient رد میکند. تنها روی پاکت. پایینتر را ببینید. |
reject هنگام نوشتن رد میشود مگر همان قانون دستکم یک شرط envelope_from داشته باشد: reject_needs_envelope، یک 422. یک 550 به کسی پاسخ میدهد که پیام را به ما داده، و روی یک فهرست پستی آن کس خودِ فهرست است، که این ردشدن را نشانهٔ مشترکی برگشتخورده میگیرد و خواننده را از چیزی لغو اشتراک میکند که فقط میخواست یک نفر در آن پست نگذارد. حتی با نوشتن آن شرط هم، تطابقی که تنها از هویتهای هدر آمده باشد به بایگانیکردن زیر Spam تنزل مییابد، چون پاکت تنها هویتی است که ردکردن میتواند صادقانه به سویش نشانه رود.
forwardِ برخاسته از قانون از مسیر ارسال بیرون میرود، که پیام را بازمیسازد: امضای DKIM اصلی باقی نمیماند، و بخشهای غیرمعمول، هدرهای نامتعارف یا هر چیزی فراتر از سقف اندازهٔ خروجی هم نمیماند، سقفی که پیام 25 MBی با پیوست از آن فراتر میرود. این نسخهای از آن چیزی است که رسیده، نه خود پیامی که رسیده. نشانی هنگام نوشتن قانون بررسی میشود، پس مقصد تأییدنشده روی خود فراخوانی 422 میگیرد نه اینکه قانونی شود که بیصدا هر دهمین پیام را میاندازد.
reply به ماشین پاسخ نمیدهد. وقتی پیام Auto-Submitted (به جز no)، Precedence: bulk|list|junk، List-Id، List-Unsubscribe، X-Autoreply یا X-Autorespond داشته باشد، وقتی فرستندهٔ پاکت خالی باشد (شکلی که هر برگشتی دارد) و وقتی هدرها اصلاً خوانده نشوند، سرکوب میشود. افزون بر آن، هر فرستنده از یک صندوق پستی مشخص در هر ۲۴ ساعت حداکثر یک پاسخ خودکار میگیرد. دو صندوق پستی با قانون پاسخ و بدون محافظ، تا وقتی کسی متوجه شود به هم نامه میدهند.