Skip to content

🧱 Overview

Mail Intake is a contract-first ingestion pipeline. Adapters handle transport to a provider, parsers normalize provider payloads, and the reader orchestrates the whole flow into canonical domain models.


🏗️ Architecture

                 ┌─────────────────────────────────────────┐
                 │  External Provider (e.g. Gmail API)     │
                 └─────────────────────┬───────────────────┘
                    ┌─────────────────▼──────────────────┐
                    │  MailIntakeAdapter (transport)      │   provider API calls
                    └─────────────────┬──────────────────┘
                                      │  provider-native payloads
                    ┌─────────────────▼──────────────────┐
                    │  Parsers (normalization)           │   headers, body, subject
                    └─────────────────┬──────────────────┘
                                      │  composed
                    ┌─────────────────▼──────────────────┐
                    │  MailIntakeReader (orchestration)  │   iter_messages / iter_threads
                    └─────────────────┬──────────────────┘
                    ┌─────────────────▼──────────────────┐
                    │  MailIntakeMessage / Thread        │   canonical domain models
                    └────────────────────────────────────┘

Layers:

  1. Adapters (mail_intake.adapters) — provider-specific, read-only transport. Return provider-native payloads; never interpret them.
  2. Auth (mail_intake.auth) — credential acquisition and lifecycle management, decoupled from adapters.
  3. Credentials (mail_intake.credentials) — persistence of auth tokens; PickleCredentialStore locally, RedisCredentialStore for production.
  4. Parsers (mail_intake.parsers) — extract headers, body text, sender, and normalized subjects from provider payloads.
  5. Ingestion (mail_intake.ingestion) — MailIntakeReader wires an adapter + parsers into iterators over canonical models.
  6. Models (mail_intake.models) — provider-agnostic MailIntakeMessage and MailIntakeThread.

📦 Domain models

MailIntakeMessage:

Field Type Meaning
message_id str Provider message id
thread_id str Conversation thread id
timestamp datetime Message timestamp
from_email str Sender email
from_name str \| None Sender display name
subject str Message subject
body_text str Extracted plain-text body
snippet str Provider snippet
raw_headers dict[str, str] Unmodified headers

MailIntakeThread:

Field Type Meaning
thread_id str Conversation id
normalized_subject str Normalized subject (threads share one)
participants set[str] Distinct senders
messages list[MailIntakeMessage] Ordered messages
last_activity_at datetime \| None Latest message time

🔒 Design guarantees

  • Read-only access — no mutation of provider state.
  • Provider-agnostic domain models.
  • Explicit configuration and dependency injection (no implicit env reads).
  • Extensible via public contracts; built-in adapters are reference implementations and may change internally.