mailnix / Routing rules

    Core concepts

    Routing rules

    Routing rules pick which destination handles each send. Without any rules, every message goes to the user's single default destination (the first active one). With rules, you can route by sender, recipient domain, category, or arbitrary metadata.

    Rule shape

    Each rule has:

    • Priority (integer): lower numbers win first. Two rules with the same priority are evaluated in creation order.
    • Match field: sender, recipient, category, or metadata.<key>. The metadata branch lets you route on any field your app sticks on the message (metadata.tenant_id, metadata.environment, etc.).
    • Match operator: equals, contains, starts_with, or regex.
    • Match value: the string to compare against. For regex, a Go-style regular expression.
    • Destination id: where matched messages go.
    • Environment (optional): live or sandbox. Only matches when the message's environment matches.
    • Enabled (boolean): toggle without deleting.

    Rules are scanned in priority order; the first match wins.

    Common patterns

    Route by category

    You want transactional sends through Postmark and marketing through Mailgun:

    priority 10  category equals "transactional"  -> postmark-prod
    priority 20  category equals "marketing"      -> mailgun-prod
    priority 99  match-all                        -> smtp-fallback

    The match-all rule is the safety net set match_field to category and match_op to contains with an empty value, or pick any field you know will be present.

    Route by recipient domain

    Internal recipients (@yourcompany.com) through your in-house SMTP host, everyone else through a cloud provider:

    priority 10  recipient contains "@yourcompany.com"  -> internal-smtp
    priority 99  category contains ""                   -> ses-prod

    Route by metadata

    You're multi-tenant and each tenant has their own destination:

    priority 10  metadata.tenant_id equals "acme"   -> acme-postmark
    priority 20  metadata.tenant_id equals "globex" -> globex-resend
    priority 99  category contains ""               -> shared-smtp

    Route by environment

    Sandbox sends through a captured-only destination, production through the real one:

    priority 10  environment = sandbox  category contains ""  -> sandbox-capture
    priority 20  environment = live     category contains ""  -> ses-prod

    Authoring rules

    The dashboard route is `/destinations/routing`. The same can be done via the MCP server:

    • email_routing_rule_create(field, op, value, destination_id, priority, environment?)
    • email_routing_rule_list() see the current chain in priority order.
    • email_routing_rule_reorder(ordered_ids) bulk-set priority ordering.
    • email_routing_rule_delete(id)

    Debugging

    The trace event routing_decision carries the matched rule's id in its payload. If a send went to the wrong destination, open the trace and look at the routing decision the rule id tells you which row won.

    If no rule matched, the payload reads reason: "default" and the destination is the user's default. Add a match-all rule at the bottom of the chain to make the routing explicit.