Customer PlaybooksSignature Stamping — Rule Priority Architecture

Signature Stamping — Rule Priority Architecture

For Exchange Online Administrators + AdtoSign Engineers · Read time: 12-15 min · Last updated: May 25, 2026

For Exchange Online Administrators + AdtoSign Engineers · Read time: 12-15 min · Last updated: May 25, 2026

This document is the deep-dive companion to Signature Stamping — Transport Rules Reference. It answers: why is the AdtoSign routing rule at priority 0, what guarantees does that give, and what happens in every edge case?

If you only need to know "what AdtoSign creates and what's safe to touch", read the reference doc instead. Read this one when something is going wrong, when you're planning a migration, or when you're auditing the design.


TL;DR

  • AdtoSign Signature Stamping rule is at priority 0 (top of the Exchange transport rule list, fires first).
  • AdtoSign signature deployment rules (AdtoSign-{name}, AdtoSign - {DG name}) are at priority 1..k (just below Signature Stamping).
  • Customer rules (DLP, encryption, archive, gateway routing) are at priority k+1+ (after all AdtoSign rules).
  • Customer rules still get first-class access to the message — but on the return pass after the gateway signs the mail.
  • The return pass is triggered by CloudServicesMailEnabled=true on the AdtoSign Inbound Connector.

The two-pass evaluation model

Exchange Online evaluates transport rules twice for AdtoSign-routed mail:

                            ┌───────────────────┐
                  PASS 1    │   User sends mail │
                            └─────────┬─────────┘
                                      ↓
                  Priority 0   ╔═════════════════╗     (fires)
                              ║ AdtoSign GW  ╠══>  X-AdtoSign-Signed=true
                               ╚═════════════════╝     route=AdtoSign Outbound
                                      ↓
                  Priority 1+  AdtoSign-* DirectHTML  (skip — header gate)
                                      ↓
                  Priority N+  Customer rules        (eval but routing locked)
                                      ↓
                            ┌───────────────────┐
                            │ Outbound Connector│
                            └─────────┬─────────┘
                                      ↓
                       sgw-{region}.adtosign.com
                                      │
                       strip mobile sigs, inject AdtoSign sig,
                       CID-embed images, stamp X-AdtoSign-Processed=true
                                      │
                                      ↓
                            ┌───────────────────┐
                            │ Inbound Connector │  CloudServicesMailEnabled=true
                            └─────────┬─────────┘     → re-evaluate all rules
                                      ↓
                  PASS 2    Priority 0   AdtoSign GW         (skip — Processed=true)
                            Priority 1+  AdtoSign-* DirectHTML (skip — Signed=true)
                            Priority N+  Customer DLP        (fires on signed body)
                            Priority N+  Customer encryption (fires)
                            Priority N+  Mimecast routing    (fires → external SMTP)
                                      ↓
                            ┌───────────────────┐
                            │   Recipient mailbox│
                            └───────────────────┘

Why two passes?

Without CloudServicesMailEnabled=true, mail returning via an Inbound Connector is treated as ALREADY-evaluated and bypasses the transport rule pipeline on the return. Customer DLP / encryption rules would then act ONLY on the unsigned outbound — they'd never see the final signed message.

CloudServicesMailEnabled=true flips that: the inbound connector tells Exchange "this is authenticated outbound mail re-entering the tenant; re-evaluate the full pipeline". This is the same mechanism used by trusted SMTP gateways like Mimecast and Proofpoint.

Why is AdtoSign at priority 0?

Before May 2026, AdtoSign was placed at N-1 (bottom). The thinking was: AdtoSign is a value-add, not a blocker, so let customer rules act first.

In practice, that broke at most enterprise customers:

  • Customer rule: priority 0-5, RouteMessageOutboundConnector=Mimecast
  • AdtoSign rule: priority N-1, RouteMessageOutboundConnector=AdtoSign Mail Flow Outbound

When Exchange evaluates conflicting RouteMessageOutboundConnector actions, the latest-evaluated rule wins (this is implementation-defined but observed consistently). So at first glance AdtoSign should win at bottom-priority.

But: several customer rules use StopRuleProcessing=true (Mimecast's recommended setting). That terminates evaluation as soon as the customer rule fires. AdtoSign at the bottom never sees the message.

Moving AdtoSign to priority 0:

  1. Guarantees AdtoSign sees every message before any customer rule can short-circuit.
  2. Lets customer rules still act on the FINAL signed mail via the return pass.
  3. Eliminates the StopRuleProcessing=true race condition entirely.

Header signaling protocol

HeaderSet byPass 1 effectPass 2 effect
X-AdtoSign-Signed: trueSignature Stamping rule (this rule), AdtoSign signature rules, gatewaySuppresses Direct HTML fallback rules below Signature Stamping (cross-rule dedup)Suppresses Direct HTML fallback rules so we don't double-sign
X-AdtoSign-Processed: trueGateway (only)—Makes the signature gateway rule skip itself on the return pass (loop prevention)
X-AdtoSign-Relay: {region}Gateway—Audit trail header — diagnostic
X-AdtoSign-Processing-Time: {ms}Gateway—Diagnostic header for support tickets
X-AdtoSign-Embedded: trueOutlook add-inGateway skips processing entirely—

These headers survive Mimecast, Proofpoint, IronPort, and other gateways by default. If your gateway strips custom headers, you'll need to allowlist X-AdtoSign-* in the gateway's policy.


Edge cases

The apply action inserts the signature gateway rule at priority 0 and Exchange renumbers all existing rules: the previous priority 0 rule becomes priority 1, etc. The relative order between non-AdtoSign rules is preserved.

If the customer rule must run BEFORE AdtoSign for compliance reasons (e.g., a legal-hold rule that must copy the original unsigned message before any modification), open a ticket — we have a per-tenant flag to opt out of priority-0 placement and revert to bottom-priority for that specific tenant. This is intentionally NOT exposed in the UI; it's an enterprise-support-only setting.

Customer DLP rejects mail on the second pass

Mail is blocked normally. From the recipient's perspective, AdtoSign was never in the path — the customer's DLP rule has the final say.

AdtoSign already processed the message and consumed quota credit. This is the correct behaviour: AdtoSign's billing reflects work done, not external delivery success.

Customer rule has StopRuleProcessing=true below Signature Stamping

That stops further rule evaluation on the FIRST pass. On the second pass, evaluation re-starts from priority 0 — so even StopRuleProcessing=true doesn't prevent the customer DLP / encryption / routing rules below from firing on the signed mail.

Customer rule has StopRuleProcessing=true ABOVE Signature Stamping

Can't happen — Signature Stamping is at priority 0, there is no rule "above" it. If a customer manually moves their rule to priority 0 (pushing Signature Stamping to priority 1), the drift cron raises FOREIGN_STOP_RULE_ABOVE_MFP within 4 hours and offers one-click repair that moves AdtoSign back to priority 0.

Customer has multiple RouteMessageOutboundConnector rules at high priority

On the FIRST pass, only the AdtoSign route action takes effect (rule at priority 0 fires first, sets the outbound connector, subsequent route actions are layered but Exchange applies the last evaluated route). The mail goes to the AdtoSign gateway.

On the SECOND pass (signed return), customer routing rules fire and direct the mail to its real destination (Mimecast, Proofpoint, etc.).

Same-tenant cross-domain mail (mohana@teamwin.in → mohana@technologicasol.com)

Both domains are accepted domains in the same M365 tenant. FromScope=InOrganization matches.

  • Pass 1: Signature Stamping fires → mail goes to gateway.
  • Gateway signs → returns to tenant.
  • Pass 2: Signature Stamping skips (Processed=true). Mail delivers to recipient mailbox in the same tenant.

No external SMTP hop, no DNS lookup — purely internal Exchange delivery on the return.

Mail from external sender (inbound to your tenant)

FromScope=InOrganization doesn't match — Signature Stamping skips. The message follows your normal inbound rules (anti-spam, transport rules, etc.). AdtoSign does not touch inbound external mail.

Mail to external recipient with delivery failure (NDR)

The NDR is generated by Exchange. AdtoSign isn't involved in the NDR path (it's a per-recipient SMTP-level rejection, not a transport-rule decision).

Gateway all-down (Tier 2 failover)

AdtoSign's failover system auto-disables the signature gateway rule (sets Mode=Audit or equivalent gated state). With Signature Stamping no longer firing:

  • X-AdtoSign-Signed is never set on outbound mail.
  • Priority 1+ AdtoSign-{name} Direct HTML rules now fire (their ExceptIf condition is no longer met).
  • Mail gets text-based HTML signatures (no CID images — image rendering quality degraded).
  • Customer rules fire normally (no second pass because no gateway round-trip).

When AdtoSign restores the signature gateway rule, Direct HTML rules go back to skipping. Seamless transition, no admin action needed.


Latency budget

StageTypical latencyWorst-case
Pass 1 transport rule evaluation< 10 ms50 ms (large rule sets)
Outbound connector → gateway (TLS, network)50-200 ms500 ms (cross-region)
Gateway processing (parse, inject, re-encode)100-400 ms1500 ms (large attachments)
Gateway → inbound connector (TLS, network)50-200 ms500 ms
Pass 2 transport rule evaluation< 10 ms50 ms
Egress (Mimecast / external SMTP)100-500 ms5000 ms (recipient server)
Total perceived latency300-1300 ms8 sec (rare)

For comparison: SMTP delivery without AdtoSign adds 100-500ms inside the tenant + however long it takes to reach the external recipient (typically 500ms-5sec). AdtoSign adds ~300-800ms in the typical case.

End users do NOT perceive this latency for sent mail because Outlook reports "sent" as soon as the message reaches the Outbox / Sent Items folder — well before the transport pipeline completes.


How AdtoSign's drift detection enforces this design

Drift cron runs every 4 hours and checks for these conditions:

Drift codeSeverityWhat happenedAuto-repair?
MFP_RULE_MISSING🔴 CriticalSignature Stamping rule was deletedYes — recreated at priority 0
MFP_RULE_DISABLED🔴 CriticalSignature Stamping rule was disabledYes — re-enabled
MFP_WRONG_PRIORITY🟡 MediumSignature Stamping rule no longer at priority 0Yes — moved back to priority 0 (Exchange renumbers other rules)
MFP_MISSING_SETHEADER🟡 MediumSignature Stamping rule lacks SetHeader X-AdtoSign-Signed=trueYes — apply adds the action
FOREIGN_STOP_RULE_ABOVE_MFP🔴 CriticalA non-AdtoSign rule sits above Signature Stamping with StopRuleProcessing=trueNo — requires manual review of the foreign rule
DIRECT_HTML_RULE_MISSING_EXCEPTIF🟡 MediumPre-Sprint-1 signature rule lacks ExceptIf X-AdtoSign-SignedYes — auto-patched on next signature redeploy
INBOUND_CLOUDSERVICESMAIL_DISABLED🔴 CriticalInbound connector's CloudServicesMailEnabled was changed to false (breaks two-pass evaluation)Yes — restored to true
OUTBOUND_CONNECTOR_MISSING/DISABLED🔴 CriticalOutbound connector gone or disabledYes — recreated/re-enabled
INBOUND_CONNECTOR_MISSING/DISABLED🔴 CriticalInbound connector gone or disabledYes — recreated/re-enabled
INBOUND_IPS_CLEARED🔴 CriticalSender IP whitelist was cleared on the inbound connectorYes — re-populated from registry

Customers receive an email alert for severity ≥ "high" (max one email per 24h to avoid spam). Drift severity color codes also appear on the deployment page banner.


FAQ

Q: Will moving AdtoSign to priority 0 affect my existing customer routing rules?

A: No. Their relative order is preserved. The apply action only inserts AdtoSign at priority 0; Exchange renumbers other rules to make room (priority 0 → 1, priority 1 → 2, etc.). Your rules continue to fire in the same relative order as before.

Q: We use Mimecast / Proofpoint / IronPort. Does the second-pass evaluation route the signed mail through the gateway?

A: Yes. As long as your gateway rule uses RouteMessageOutboundConnector (not StopRuleProcessing=true as the only action), the second-pass evaluation will fire your routing rule on the signed mail. The mail flows: User → Exchange → AdtoSign gateway → Exchange (second pass) → Mimecast → Recipient.

Q: What if my DLP rule needs to scan the UNSIGNED original message (not the signed return)?

A: Less common, but possible. Open a ticket — we can enable the per-tenant dual_evaluation_mode = false flag which reverts to bottom-priority placement. Trade-off: signature injection becomes best-effort instead of guaranteed.

Q: Can I see what priority a tenant's Signature Stamping rule is currently at?

A: Yes. The Function App's inspect action returns result.transportRule.priority for any orgId. The deployment page in the AdtoSign UI also shows this in the rule details panel.

Q: How long does the apply migration take for an existing tenant?

A: 10-30 seconds typical. Most of the time is spent waiting for Exchange Online's transport-rule replication (5-60s observed). The function has retry logic for replication lag at ConfigureMailFlowConnectors/function.ps1:969.

Q: Will signature rules at priority 1+ ever interfere with the gateway?

A: No. With the SetHeader X-AdtoSign-Signed=true action on the signature gateway rule (priority 0), every downstream Direct HTML rule's ExceptIf X-AdtoSign-Signed condition is met, so they all skip. The gateway handles the entire signing flow.

Q: What's the upgrade path for existing tenants?

A: One-time action=apply call per tenant. Documented in changelog/2026-05-25-mfp-priority-zero-placement.md. We're rolling this out tenant-by-tenant; super-admins can monitor via the admin panel.