Signup protection Pricing Documentation Sign up Log in

Gates

Rules & conditions

A gate is a stack of rules. Each rule checks one or more conditions and suggests an action: allow, block or challenge. The evaluation model below explains how the gate turns matching rules into one decision.

Anatomy of a rule

Each rule has these fields:

Field Type Required Description
name string yes Short, descriptive label shown in the UI and logs
description string no Why this rule exists or what it targets
message string no Returned in the decision when this rule sets the verdict
match enum: all, any no all = every condition must be true, any = at least one must be. Defaults to all when you create a rule.
action enum: allow, block, challenge yes The outcome when the rule matches
stop boolean no If true, stop evaluating further rules when this one matches. Defaults to false. Shown as Stop processing in the dashboard.
enabled boolean no If false, the rule is ignored. Defaults to true.
conditions array<Condition> yes One or more field comparisons

Evaluation model

  1. Rules are processed top to bottom. Disabled rules are skipped.
  2. For each rule, its conditions are evaluated using the rule's match mode (all or any).
  3. If the rule matches, its action becomes the current decision. A later matching rule replaces it.
  4. If a matching rule has stop: true, evaluation ends and its action is final.
  5. If no rule matched, the gate's default_action is returned and rule is null in the response.

So when every rule has stop: true, the first matching rule decides. When no rule has it, the last matching rule decides.

Rule order

A gate created in the dashboard starts with six starter rules, and all of them have Stop processing on (the rule list shows Exit on match). So in a new dashboard gate, the first matching rule decides. A gate created through the Management API starts with no rules.

New rules, from the dashboard or the API, are added at the bottom, with Stop off unless you turn it on.

To make an exception win, such as always allowing a partner's domain:

  • Turn Stop on for it and place it above the rules it overrides. Use this on any gate created in the dashboard.
  • Or, if none of the rules use Stop, place it below them. The last match wins.

An exception added at the bottom of a gate whose rules use Stop never runs for the cases it was meant to cover: a rule above it has already matched and ended evaluation. Drag rules to reorder them in the dashboard, or use the reorder endpoint.

Conditions

A condition is an object with three keys: the field to read, the op to apply, and the value to compare against.

{ "field": "domain.tld", "op": "in", "value": ["xyz", "top"] }

Condition operators

Which operators a field accepts depends on its type, shown in the field tables below.

Operator Matches when the field Field types
eq equals the value boolean, number, string
ne does not equal the value boolean, number, string
lt, lte, gt, gte is less than, at most, greater than, or at least the value number
contains contains the value string
starts_with starts with the value string
ends_with ends with the value string
in equals one of the values in the list string
not_in equals none of the values in the list string
regex matches the pattern string
not_regex does not match the pattern string
is_null has no value string
not_null has a value string

The value follows the field and operator:

  • Boolean fields take true or false. Number fields take an integer.
  • in and not_in take a list of strings, as in the example above. Number fields have no in: to match several ASNs, write a match: any rule with one eq condition per ASN.
  • is_null and not_null take no value.
  • A string value, each item of an in list, and a pattern can each be up to 255 characters.
  • String comparisons are case-insensitive, for every operator.

regex and not_regex take a bare pattern, such as ^admin$. Do not wrap it in slashes or add flags: /^admin$/ is read as a pattern that includes the slashes. Inline options such as (?-i) work. Length checks such as ^.{21,}$ count bytes, not characters, so accented or non-Latin text reaches the limit sooner.

A pattern is checked for errors when you save the rule. If a valid pattern still fails on a particular value during a decision, for example because it backtracks too much on a long input, the condition does not match, whether it uses regex or not_regex. Keep patterns short and anchored (^...$), and avoid nested repetition such as ([a-z]+)+.

Common recipes

Each recipe is a rule body you can send to Add a rule, or build the same way in the dashboard. They all use stop: true, like the starter rules, so the first matching rule decides and exceptions go at the top.

Block disposable addresses

{
  "name": "Block disposable",
  "match": "any",
  "action": "block",
  "stop": true,
  "conditions": [
    { "field": "email.disposable", "op": "eq", "value": true },
    { "field": "domain.disposable", "op": "eq", "value": true }
  ]
}

email.disposable also catches known throwaway addresses on ordinary domains. domain.disposable covers requests that send only a domain. Gates created in the dashboard start with this rule.

Challenge relay and forwarding addresses

{
  "name": "Challenge relays",
  "match": "all",
  "action": "challenge",
  "stop": true,
  "conditions": [
    { "field": "domain.relay_domain", "op": "eq", "value": true }
  ]
}

Relay addresses such as Apple's Hide My Email forward to a real inbox, so a challenge usually fits better than a block. See Relay Domains.

Always allow partner domains

{
  "name": "Allow partners",
  "match": "any",
  "action": "allow",
  "stop": true,
  "conditions": [
    { "field": "domain.name", "op": "in", "value": ["partner.com", "partner.co.uk"] },
    { "field": "domain.name", "op": "ends_with", "value": ".partner.com" }
  ]
}

Move this rule to the top of the gate, above the rules it should override. The ends_with condition covers subdomains such as mail.partner.com. The leading dot keeps it from matching otherpartner.com, and is why partner.com itself is in the list.

Block datacenter IPs without blocking iCloud Private Relay

{
  "name": "Block datacenter IPs",
  "match": "all",
  "action": "block",
  "stop": true,
  "conditions": [
    { "field": "ip.hosting.detected", "op": "eq", "value": true },
    { "field": "ip.anonymity.relay.detected", "op": "eq", "value": false }
  ]
}

iCloud Private Relay sends Safari users' traffic through hosting IPs, so ip.hosting.detected alone would also block them. The second condition lets them through. The rule only applies to requests that send an ip.

Supported fields

A condition can read any of the fields below.

Which groups are populated depends on what the decision request carried. The domain.* fields are always present. The email.* fields are only present when the request sent an email, and the ip.* fields only when it sent an ip. An absent field reads as null, which satisfies is_null, ne, not_in and not_regex. To make a rule apply only when the input was sent, add an email.address not_null or ip.address not_null condition to it. Fields written with a * hold a list rather than a single value and follow the separate rules in Multi-value fields.

Email fields

Field Type Details
email.address string The full email address.
email.normalized string The canonical form of the address. See Email Normalization.
email.local_part string Local part of the email address. For example, user in [email protected].
email.subaddress string Subaddress of the email address. For example, newsletter in [email protected].
email.disposable boolean The address is disposable, either because its domain is a disposable service or because the specific address is a known throwaway on an ordinary domain (e.g. Gmail or Outlook).
email.role_account boolean Local part is a role account (admin@, support@). See Role Accounts.

Domain fields

Field Type Details
domain.name string The full domain name. Use sparingly, for one-off allow or block entries.
domain.tld string The public suffix, which can have several parts: com in example.com, co.uk in acme.co.uk.
domain.sld string The label just before the suffix: acme in mail.acme.co.uk.
domain.subdomain string Everything before the SLD, or null: mail in mail.acme.co.uk.
domain.authority number A 0–100 score indicating how established and credible a domain is; null when no score is available. Higher scores indicate well-established domains. Lower scores may signal newly created or less reputable domains. See Domain Authority.
domain.tld_trust number A 1–5 score indicating how strict the TLD's registration requirements are. Higher scores mean the registry performed more identity verification. null for a suffix we have not scored. See TLD Trust.
domain.age_days number Days since the domain was registered. null when the registration date is unknown.
domain.mx boolean Domain has valid MX records. null when the DNS lookup got no answer.
domain.mx_records.*.hostname string Hostnames of MX records. Useful for filtering based on MX record hostnames.
domain.mx_providers.*.slug string Slug identifiers of detected MX providers. Useful for filtering by specific provider (e.g., google, mimecast). See MX Providers.
domain.mx_providers.*.type string Category of MX provider. Values: mailbox, hosting, email_api, security_gateway, forwarding.
domain.mx_providers.*.grade string Provider grade. Values: enterprise, professional, standard, basic.
domain.spf string The domain's SPF posture. Values: strict, relaxed, neutral, permissive, missing, invalid, or null when not yet known. See SPF & DMARC.
domain.dmarc string The domain's DMARC policy. Values: reject, quarantine, none, missing, invalid, or null when not yet known. See SPF & DMARC.
domain.disposable boolean Domain is a disposable email provider. See Disposable Domains.
domain.disposable_provider string The service operating the domain. Only present when domain.disposable is true, and null even then when the operator is unidentified.
domain.public_domain boolean Domain is a public mailbox provider (gmail.com, outlook.com, yahoo.com). See Public Domains.
domain.relay_domain boolean Domain's mail is handled by a forwarding or relay service. See Relay Domains.
domain.free_subdomain boolean Domain is a subdomain of a free subdomain or dynamic DNS service like duckdns.org. The parent domain itself is not flagged. See Free Subdomains.
domain.spam boolean Domain flagged for spam or other abusive activity. See Spam Domains.
domain.blocklisted boolean Domain is present in your account blocklist.

Unknown values

A signal we have no data for is null, not zero or false. This affects domain.authority, domain.age_days and domain.tld_trust when unknown, domain.spf and domain.dmarc before we have checked the domain (which can happen the first time we see it), and domain.mx when the DNS lookup got no answer.

A null never satisfies eq, lt, lte, gt, gte, in, contains, starts_with or ends_with. It does satisfy ne and not_in. regex and not_regex read it as empty text, so not_regex matches it unless the pattern also matches empty text. So:

  • An authority-below-10 rule skips domains with no score, because domain.authority lt 10 never matches null. The same applies to an unknown domain.age_days.
  • domain.mx eq false lets a domain through when its DNS lookup got no answer, while domain.mx ne true catches it.

is_null and not_null are available only for string fields. Number and boolean fields do not accept them.

Multi-value fields

Four fields hold a list rather than a single value: domain.mx_records.*.hostname, domain.mx_providers.*.slug, domain.mx_providers.*.type and domain.mx_providers.*.grade. Most domains publish several MX records, and a domain can resolve to more than one provider: Google Workspace behind a Mimecast gateway is both.

A condition on one of these reads the whole list:

  • Positive operators (eq, in, contains, starts_with, ends_with, regex) match when any entry satisfies them.
  • Negative operators (ne, not_in, not_regex) match when every entry satisfies them, that is, when no entry matches the value.

For a domain resolving to both google and mimecast:

Condition Result
domain.mx_providers.*.slug eq google matches
domain.mx_providers.*.slug ne google does not match
domain.mx_providers.*.slug not_in ["google"] does not match

An empty list matches nothing. When we recognise no provider for a domain, domain.mx_providers.*.slug is an empty list and every comparison against it is false, ne and not_in included. Test for it with is_null: on a multi-value field, is_null means "no entry has a value" and not_null means "at least one entry has a value".

The distinction matters when the list is present but the values are not. domain.mx_providers.*.slug and domain.mx_providers.*.type always carry a value, so for those fields "no entry has a value" and "the list is empty" are the same thing. domain.mx_providers.*.grade does not: a provider we recognise but have not graded contributes an entry whose value is null. A grade is_null condition therefore matches both "we recognise no provider" and "we recognise one but have no grade for it". If you mean only the first, test slug or type rather than grade.

That makes "only allow Google Workspace" two conditions rather than one:

{
  "match": "any",
  "action": "block",
  "conditions": [
    { "field": "domain.mx_providers.*.slug", "op": "ne", "value": "google" },
    { "field": "domain.mx_providers.*.slug", "op": "is_null" }
  ]
}

Without the second condition the rule allows every domain whose mail provider we cannot identify.

Conditions are not tied to the same entry. Each is evaluated against the whole list on its own. In a match: all rule, grade eq basic alongside slug ne yahoo asks "some provider is basic" and "no provider is Yahoo", not "some provider is basic and is not Yahoo". A domain served by both Yahoo and another basic-grade provider satisfies the first condition and fails the second, so the rule does not fire.

IP fields

Field Type Details
ip.address string The IP address.
ip.abuse.detected boolean IP address has been flagged for abuse.
ip.anonymity.proxy.detected boolean IP address is detected as a proxy.
ip.anonymity.relay.detected boolean The IP is an exit point of a privacy relay. Currently this means iCloud Private Relay.
ip.anonymity.relay.provider string The relay's name, currently always iCloud Private Relay. null when the IP is not a relay.
ip.anonymity.tor.detected boolean IP address is detected as a Tor exit node.
ip.anonymity.vpn.detected boolean IP address is detected as a VPN.
ip.geo.city string The city where the IP is located.
ip.geo.continent string The continent name, for example North America.
ip.geo.continent_code string The two-letter continent code, for example NA or EU.
ip.geo.country string The country name, for example United States.
ip.geo.country_code string The two-letter ISO country code, for example US.
ip.hosting.detected boolean The IP belongs to a hosting or datacenter range. iCloud Private Relay IPs are hosting IPs too, so a rule that blocks on this field also blocks Safari users with iCloud+. Add ip.anonymity.relay.detected eq false to the same match: all rule to exclude them, as in this recipe.
ip.network.asn number The Autonomous System Number (ASN) of the IP's network.
ip.network.aso string The Autonomous System Organization (ASO) name.
ip.network.domain string The domain associated with the IP's network.
ip.network.type string The network type reported for the IP's ASN, for example hosting.
Previous
Quickstart