# Out-of-office detection

- **Pattern:** `ab-000611` (`campaign-orchestration-sequencing.reply-engagement.ooo-detection`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000611
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000611)

## Why it matters

An OOO auto-responder and a genuine human reply are structurally identical inbound emails. Without explicit OOO detection, the system treats every `Auto-Submitted: auto-replied` as a real engagement signal — permanently pausing sequences, creating rep notifications, and potentially marking the contact as 'replied' when they are simply on vacation. The inverse failure is equally bad: some implementations silently discard all inbound emails to avoid OOO noise, missing genuine replies in the process. iso-25010:2011 reliability.fault-tolerance requires that the system handle this expected abnormal input class explicitly, not by accident.

## Severity rationale

Medium because undetected OOO messages either incorrectly terminate sequences (false-positive reply) or create spurious rep notifications, degrading signal quality and operational trust.

## Remediation

Check IETF-standard headers before routing an inbound email to reply handling. The `Auto-Submitted` header is the authoritative signal; subject/body patterns catch non-compliant mail clients.

```ts
function isOutOfOffice(headers: Record<string, string>, subject: string, body: string): boolean {
  if (headers['auto-submitted'] && headers['auto-submitted'] !== 'no') return true
  if (headers['x-autoreply']) return true
  const oooPatterns = [/out of office/i, /on vacation/i, /automatic reply/i, /auto-reply/i, /away from/i]
  return oooPatterns.some(p => p.test(subject) || p.test(body.slice(0, 500)))
}

async function handleInboundEmail(email: InboundEmail) {
  if (isOutOfOffice(email.headers, email.subject, email.body)) {
    // Temporarily defer — do NOT mark as replied or notify reps
    const returnDate = extractReturnDate(email.body) ?? addDays(new Date(), 7)
    await deferEnrollmentUntil(email.contactId, returnDate)
    return
  }
  await handleReply(email)
}
```

Check at least two signal types — `Auto-Submitted` header plus at least one subject/body pattern — to handle both compliant and non-compliant mail servers.

## Detection

- **ID:** `ooo-detection`
- **Severity:** `medium`
- **What to look for:** When inbound email processing is in place, check whether the system distinguishes between genuine replies and auto-responders (out-of-office, vacation replies). OOO messages should not count as engagement (they should not advance a contact to a different branch or trigger rep notifications), but the sequence should be temporarily paused or the next step delayed until after the return date if detectable. Look for: pattern matching on `Auto-Submitted` headers, `X-Autoreply` headers, or subject line keywords (Re: Out of Office, "I'm out of office", "automatic reply"); and different handling paths for OOO versus real replies.
- **Pass criteria:** Inbound email handler identifies OOO messages by headers or content patterns. OOO messages trigger a temporary pause (not a permanent reply exit) with a reschedule for after the return date, or are silently ignored without counting as engagement. Count the OOO detection signals checked (headers, subject patterns, body patterns) — at least 2 must be inspected.
- **Fail criteria:** All inbound emails are treated identically regardless of whether they are genuine replies or OOO auto-responders. OOO messages either incorrectly pause the sequence permanently or incorrectly count as engagement signals.
- **Skip (N/A) when:** Reply detection is not implemented.
- **Detail on fail:** `"All inbound emails processed as genuine replies — OOO auto-responses pause the sequence permanently and trigger rep notifications"` or `"No Auto-Submitted header inspection — no OOO detection"`
- **Remediation:** Check OOO headers before processing a reply:

  ```ts
  function isOutOfOffice(headers: Record<string, string>, subject: string, body: string): boolean {
    if (headers['auto-submitted'] && headers['auto-submitted'] !== 'no') return true
    if (headers['x-autoreply']) return true
    const oooPatterns = [/out of office/i, /on vacation/i, /automatic reply/i, /auto-reply/i, /away from/i]
    return oooPatterns.some(p => p.test(subject) || p.test(body.slice(0, 500)))
  }

  async function handleInboundEmail(email: InboundEmail) {
    if (isOutOfOffice(email.headers, email.subject, email.body)) {
      // Silently pause or defer — do NOT count as engagement
      await deferEnrollmentUntil(email.contactId, extractReturnDate(email.body) || addDays(new Date(), 7))
      return
    }
    await handleReply(email)
  }
  ```

## External references

- iso-25010 reliability.fault-tolerance

Taxons: error-resilience

HTML version: https://auditbuffet.com/patterns/ab-000611
