Mail filters (Sieve)
Filters decide what happens to mail as it arrives: file it in a folder, flag it, forward it, refuse it or throw it away. Mailocity runs filters written in Sieve (RFC 5228), the standard filter language that most mail servers and many mail apps understand. Every user can have their own filters, and administrators can add rules for a whole tenant or domain.
The webmail rule builder ("when the sender is X, move it to folder Y") and the matching screens in the admin panels arrive in M2. Until then, filters are managed with mail apps that speak ManageSieve or JMAP Sieve, or through the API.
How filters run
When a message arrives for one of your users (from another server, or from a colleague on the same server), up to three scripts run in this order:
- Tenant rules, set by the tenant's administrators, for every account in the tenant.
- Domain rules, for mail addressed to that domain. The platform sets the rules of shared domains.
- The user's active script. A user can keep up to 20 scripts and turn one of them on.
A script that decides where the message goes (it files it, discards it, redirects it, rejects it or keeps it explicitly) ends the chain: later scripts do not run. A script that only sets flags, or does nothing, passes the message and its flags to the next one. Actions with :copy add a copy without ending the chain. If nothing decides, the message goes where it would have gone without filters: the inbox, the plus-address folder, or Junk when it failed the sender's DMARC policy.
Filters never lose mail by accident: a script that fails to run, a folder that does not exist, or a redirect that is not allowed all leave the message in the default folder.
What scripts can do
Supported extensions: fileinto, envelope, imap4flags, copy, reject and ereject, body, variables, relational, subaddress, environment, encoded-character, date, mailbox, vacation and vacation-seconds (see Vacation replies), and the comparators i;octet, i;ascii-casemap, i;ascii-numeric and i;unicode-casemap.
An example user script:
require ["fileinto", "imap4flags", "copy", "subaddress", "envelope"];
# Newsletters go to their own folder, already read.
if header :contains "list-id" "newsletter" {
fileinto :flags "\\Seen" "Newsletters";
stop;
}
# Mail to alice+invoices@ goes to Invoices.
if envelope :detail "to" "invoices" {
fileinto "Invoices";
}
# Anything from the boss is flagged and also sent to my assistant.
if address :is "from" "boss@example.com" {
addflag "\\Flagged";
redirect :copy "assistant@example.com";
}
And a tenant rule that flags urgent mail for everyone without filing it, so each user's own filters still run:
require "imap4flags";
if header :contains "subject" "[URGENT]" { addflag "\\Flagged"; }
Notes on matching:
- Header fields are decoded before they are compared, so
header :contains "subject" "Café"matches an encoded subject. - The default comparator,
i;ascii-casemap, ignores case only for the letters A to Z. To ignore case in other scripts too (É and é), add:comparator "i;unicode-casemap"and requirecomparator-i;unicode-casemap. body :textsearches text parts and HTML parts with the markup removed.
Dates and folders
date tests a date in a header field, and currentdate the time the message arrives (RFC 5260). Dates are compared in UTC unless the test gives :zone "+0200", or :originalzone to keep the header's own zone. For Received fields the date after the last ; is used. The parts are year, month, day, date (2026-10-06), julian, hour, minute, second, time (14:05:00), iso8601, std11, zone (+0200) and weekday (0 is Sunday). With relational, :value "ge" and :value "le" give ranges:
require ["date", "relational", "fileinto"];
# During October, file mail about the conference in its own folder.
if allof(currentdate :value "ge" "date" "2026-10-01",
currentdate :value "le" "date" "2026-10-31",
header :contains "subject" "conference") {
fileinto "Conference";
}
mailbox (RFC 5490) adds fileinto :create, which creates the folder when it does not exist, and the mailboxexists test:
require ["mailbox", "fileinto", "variables"];
# One folder per project, created on first use.
if header :matches "subject" "[project *]*" {
fileinto :create "Projects/${1}";
}
A folder is created only when its name is allowed (not INBOX, no *, % or line breaks, at most 255 bytes) and the account has fewer than 200 folders; otherwise the message goes to the default folder. Without :create, a missing folder still means the default folder.
Forwarding (redirect)
redirectto an address on this server stores the message in that account's inbox right away. That account's own filters do not run, so two accounts redirecting to each other cannot loop.redirectto another domain needs the Forwarding entitlement on the tenant's plan (Solo and Team have it, Free does not). It counts against the forwarding user's sending limits.- The forwarded message goes out with an SRS envelope sender on the user's domain, such as
SRS0=hHq4=7K=sender.example=news@example.com: the next server's SPF check sees this server's domain and passes, and a bounce sent back to that address is relayed to the original sender (here news@sender.example), as if the message had not been forwarded. These addresses are signed by this server, accept only bounces (mail from the null sender), and expire after 21 days. - Forwarded copies carry a
Delivered-Toheader naming the user. A message that already went through that user, or has passed through more than 25 servers, is not redirected again. A user cannot redirect to their own address. - At most 5 redirects per message.
- Copies forwarded to another domain carry an ARC set signed with the user's domain key, recording the SPF, DKIM and DMARC results the message had when it arrived, so the receiving provider can trust them. See ARC.
Reject
reject and ereject refuse the message with the script's reason. When everyone the message was addressed to on this server rejects it, the sending server is told during the SMTP conversation and returns it to the sender. When only some of them reject it, the others still get it and the rejecting users' copies are dropped without telling the sender.
Managing filters
Users, in webmail: open Filters (the sliders button at the top).
- Rules is a builder for everyday filters. Each rule has conditions (From, To or Cc, Subject, the +tag the message was sent to, any header, or the size) with contains, is, starts with, ends with or does not contain, matched all together or any one; and actions: move or copy to a folder (created if it does not exist), mark as read, star, forward or forward a copy, delete, refuse with a message, or stop. Rules run top to bottom; you can reorder and pause them. Saving writes a Sieve script called
webmail-rulesand turns it on. - Advanced: write your own Sieve script is an editor for the full language. Check validates it on the server first, and saving turns it on. Only one script is in use at a time: saving one turns the other off, which stays saved and can be switched back on.
Scripts made in a mail app over ManageSieve show up in the editor, and the builder's rules show up in mail apps as the webmail-rules script. Editing webmail-rules by hand is possible, but the builder rewrites it the next time its rules are saved.
Users, from a mail app that speaks ManageSieve (RFC 5804), such as Thunderbird with the Sieve add-on, Roundcube's managesieve plugin or sieve-connect:
| Setting | Value |
|---|---|
| Server | the mail server's host name, the same one used for IMAP |
| Port | 4190 |
| Security | STARTTLS |
| User name and password | the full email address and its password |
Apps that look up the _sieve._tcp SRV record find the server and port on their own when the domain publishes it (it is in the domain's DNS records list). The app can upload, rename, delete and check scripts and choose the active one; these are the same scripts JMAP Sieve and the API manage. A script that does not compile is refused with the line and column of the problem. The active script cannot be deleted until another one is turned on or filters are turned off. ManageSieve is not tied to a plan feature, just as JMAP Sieve is not. Outside development mode the server asks for STARTTLS before it accepts a password.
Users, from a mail app that supports JMAP Sieve (RFC 9661): upload the script as a blob, then create it with SieveScript/set and turn it on with onSuccessActivateScript. SieveScript/validate checks a script without saving it, and SieveScript/get and SieveScript/query list them. The active script cannot be destroyed until another one is turned on or onSuccessDeactivateScript turns filters off.
Administrators, through the API:
| Request | What it does |
|---|---|
POST /v1/sieve/validate | Checks a script and says what is wrong, with the line and column. |
GET / PUT /v1/tenants/{id}/sieve | The tenant rules. PUT with {"script": ""} removes them. |
GET / PUT /v1/tenants/{id}/domains/{domain}/sieve | A domain's rules. |
GET / PUT /v1/shared-domains/{domain}/sieve | A shared domain's rules. |
GET / POST /v1/tenants/{id}/accounts/{accountId}/sieve-scripts | A user's scripts; create one, optionally "active": true. |
GET / PATCH / DELETE .../sieve-scripts/{scriptId} | Read, rename, replace, turn on or off ("active"), or delete one. |
A script that does not compile is refused with a 422 naming the problem, so a typo never reaches delivery.
Limits
- Scripts up to 64 KiB; names up to 128 characters; 20 scripts per user.
- ManageSieve: lines up to 8 KiB, a session ends after 10 failed commands or 3 failed sign-ins, and after 1 minute idle before signing in or 10 minutes after.
- 5 redirects per message. Each filter run is stopped after 5 seconds.
- Body tests search at most 100 MIME parts, 8 levels deep and 8 MiB of decoded text.
Not yet
- ManageSieve's optional
UNAUTHENTICATE,OWNERand SASL mechanisms other thanPLAIN, and implicit TLS on 4190. - The
regex,include,indexandeditheaderextensions, the mailbox extension's metadata tests, and notifications. - SRS for forwarded mail. Forwarded mail is sent from the user's own address, which passes SPF, and carries an ARC set (see below).
- The rule builder and raw editor in webmail and the admin panels (M2).