← Back to docs

Inbound Rules

Inbound Rules

Inbound rules let you automatically filter, route, and process incoming emails based on sender, subject, headers, and authentication results. Rules extend your existing alias routing — they evaluate before the alias default action and can override it.

How Rules Work

When an email arrives at one of your aliases, Postscale evaluates rules in priority order:

  1. Alias-specific rules run first (rules tied to the receiving alias)
  2. Domain-wide rules run next (rules with no alias specified)
  3. The first matching rule's action is applied
  4. If no rule matches, the alias default action runs (forward, webhook, etc.)
Non-destructive

Rules extend aliases — they never replace your existing routing. If no rules match, everything works exactly as before.

Creating a Rule

curl -X POST "https://api.postscale.io/v1/domains/{domain_id}/rules" \
  -H "Authorization: Bearer ps_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Block spam domain",
    "conditions": [
      {"field": "sender_domain", "comparator": "equals", "value": "spam.com"}
    ],
    "action": "drop",
    "priority": 10
  }'

Or with the SDK:

await postscale.rules.create(domainId, {
  name: 'Block spam domain',
  conditions: [
    { field: 'sender_domain', comparator: 'equals', value: 'spam.com' }
  ],
  action: 'drop',
  priority: 10
});

Condition Fields

Each condition matches against a part of the incoming email:

FieldWhat it matchesExample
senderFull sender addressspam@bad.com
sender_domainDomain part of sendermarketing.com
subjectSubject lineURGENT, Invoice
headerAny email header (specify header_name)X-Mailer: BulkMailer
spfSPF authentication resultpass, fail, softfail, none
dkimDKIM authentication resultpass, fail, none

Comparators

All comparators are case-insensitive except matches.

ComparatorDescription
equalsExact match
not_equalsNot equal
containsSubstring present
not_containsSubstring absent
starts_withBegins with value
ends_withEnds with value
matchesRegular expression

Actions

ActionWhat happens
dropSilently discard the email
storeStore in DB without forwarding or webhooks
mark_spamFlag as spam, then continue to alias default action
forwardForward to specific addresses (overrides alias)
webhookDeliver to a specific webhook (overrides alias)

Combining Conditions

Rules support AND/OR logic via the condition_match field:

  • "all" (default) — every condition must match (AND)
  • "any" — at least one condition must match (OR)

You can add up to 10 conditions per rule.


Examples

Block a Spam Domain

Drop all emails from a known spam domain:

curl -X POST "https://api.postscale.io/v1/domains/{domain_id}/rules" \
  -H "Authorization: Bearer ps_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Block spam.com",
    "conditions": [
      {"field": "sender_domain", "comparator": "equals", "value": "spam.com"}
    ],
    "action": "drop",
    "priority": 10
  }'

Block Emails Failing SPF and DKIM

Drop emails that fail both SPF and DKIM authentication:

curl -X POST "https://api.postscale.io/v1/domains/{domain_id}/rules" \
  -H "Authorization: Bearer ps_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Block unauthenticated senders",
    "conditions": [
      {"field": "spf", "comparator": "not_equals", "value": "pass"},
      {"field": "dkim", "comparator": "not_equals", "value": "pass"}
    ],
    "condition_match": "all",
    "action": "drop",
    "priority": 20
  }'

Route Marketing Emails to a Webhook

Forward emails from a marketing domain to a specific webhook for processing:

curl -X POST "https://api.postscale.io/v1/domains/{domain_id}/rules" \
  -H "Authorization: Bearer ps_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Marketing emails to webhook",
    "conditions": [
      {"field": "sender_domain", "comparator": "equals", "value": "marketing.example.com"}
    ],
    "action": "webhook",
    "action_config": {
      "webhook_id": "your-webhook-uuid"
    },
    "priority": 30
  }'

Forward Urgent Emails to Multiple Addresses

Catch any email with "urgent" or "critical" in the subject and forward to the on-call team:

curl -X POST "https://api.postscale.io/v1/domains/{domain_id}/rules" \
  -H "Authorization: Bearer ps_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Urgent to on-call",
    "conditions": [
      {"field": "subject", "comparator": "contains", "value": "urgent"},
      {"field": "subject", "comparator": "contains", "value": "critical"}
    ],
    "condition_match": "any",
    "action": "forward",
    "action_config": {
      "forward_to": ["oncall@company.com", "alerts@company.com"]
    },
    "priority": 5
  }'

Block Bulk Mailers by Header

Drop emails sent by known bulk mailing software:

curl -X POST "https://api.postscale.io/v1/domains/{domain_id}/rules" \
  -H "Authorization: Bearer ps_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Block bulk mailer",
    "conditions": [
      {
        "field": "header",
        "comparator": "contains",
        "value": "BulkMailer",
        "header_name": "X-Mailer"
      }
    ],
    "action": "drop",
    "priority": 15
  }'

Block Senders with Regex

Use a regex pattern to block multiple spam domains at once:

curl -X POST "https://api.postscale.io/v1/domains/{domain_id}/rules" \
  -H "Authorization: Bearer ps_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Regex block spam domains",
    "conditions": [
      {
        "field": "sender",
        "comparator": "matches",
        "value": ".*@(spam|junk|promo)\\.com
quot; } ], "action": "drop", "priority": 10 }'

Store Newsletters Without Forwarding

Keep newsletter emails in your Postscale inbox without forwarding them:

curl -X POST "https://api.postscale.io/v1/domains/{domain_id}/rules" \
  -H "Authorization: Bearer ps_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Store newsletters",
    "conditions": [
      {"field": "subject", "comparator": "contains", "value": "newsletter"},
      {"field": "header", "comparator": "contains", "value": "list-unsubscribe", "header_name": "List-Unsubscribe"}
    ],
    "condition_match": "any",
    "action": "store",
    "priority": 50
  }'

Alias-Specific Rule

Attach a rule to a specific alias instead of the whole domain:

curl -X POST "https://api.postscale.io/v1/domains/{domain_id}/rules" \
  -H "Authorization: Bearer ps_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "VIP senders only for support@",
    "alias_id": "your-alias-uuid",
    "conditions": [
      {"field": "sender_domain", "comparator": "not_equals", "value": "bigclient.com"}
    ],
    "action": "drop",
    "priority": 1
  }'

Testing Rules

Before emails start flowing, test your rules against sample data to verify they work as expected. The test endpoint evaluates all rules and shows per-condition match details:

curl -X POST "https://api.postscale.io/v1/domains/{domain_id}/rules/test" \
  -H "Authorization: Bearer ps_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "user@spam.com",
    "subject": "Buy cheap stuff now!!!",
    "spf": "fail",
    "dkim": "fail"
  }'

Response:

{
  "matched_rules": [
    {
      "rule_id": "...",
      "rule_name": "Block spam.com",
      "priority": 10,
      "action": "drop",
      "stop_processing": true,
      "matched": true,
      "conditions": [
        {
          "field": "sender_domain",
          "comparator": "equals",
          "value": "spam.com",
          "matched": true,
          "actual": "spam.com"
        }
      ]
    }
  ],
  "effective_action": "drop",
  "effective_rule_id": "...",
  "would_fall_through": false
}

The would_fall_through field tells you whether the email would reach the alias default action (no rule matched or no rule stopped processing).

Dry run

The test endpoint never affects real emails. Use it to debug complex rule sets before they go live.

Managing Rules

List Rules

curl "https://api.postscale.io/v1/domains/{domain_id}/rules" \
  -H "Authorization: Bearer ps_live_your_api_key"

Update a Rule

Partial updates are supported — only include the fields you want to change:

curl -X PUT "https://api.postscale.io/v1/rules/{rule_id}" \
  -H "Authorization: Bearer ps_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "active": false
  }'

Delete a Rule

curl -X DELETE "https://api.postscale.io/v1/rules/{rule_id}" \
  -H "Authorization: Bearer ps_live_your_api_key"

Reorder Rules

Change the evaluation order by setting new priorities:

curl -X PUT "https://api.postscale.io/v1/domains/{domain_id}/rules/reorder" \
  -H "Authorization: Bearer ps_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "rules": [
      {"id": "rule-1-uuid", "priority": 10},
      {"id": "rule-2-uuid", "priority": 20},
      {"id": "rule-3-uuid", "priority": 30}
    ]
  }'

Priority and Stop Processing

  • Priority controls evaluation order. Lower numbers run first (0-1000, default 100).
  • Stop processing (default true) determines whether to stop evaluating further rules after a match. Set to false to allow multiple rules to match — the last matching rule's action wins.
Rule ordering tip

Put your most specific rules at low priorities (e.g., 1-10) and catch-all rules at higher priorities (e.g., 100+). This ensures specific rules always take precedence.

Plan Limits

PlanMax rules per domain
Free5
Starter25
Plus50
Pro100
Scale500

Match Statistics

Every rule tracks how many emails it has matched and when it last matched. Use this to identify rules that are no longer effective or to monitor rule activity via the list endpoint.