1521 lines
55 KiB
Markdown
1521 lines
55 KiB
Markdown
|
|
# EmailAdapterSpecification.md
|
|||
|
|
|
|||
|
|
## 1. Document Status
|
|||
|
|
|
|||
|
|
**Document:** EmailAdapterSpecification.md
|
|||
|
|
**Project:** email-connect
|
|||
|
|
**Target Integration:** coordination-engine
|
|||
|
|
**Adapter Contract:** AdapterInterfaceSpecification.md v1.0
|
|||
|
|
**Status:** Draft v1.0
|
|||
|
|
**Primary Scope:** Email protocol and provider integration as a coordination-engine adapter
|
|||
|
|
|
|||
|
|
## 2. Purpose
|
|||
|
|
|
|||
|
|
This document specifies how `email-connect` models email as a communication protocol and how it integrates with `coordination-engine`.
|
|||
|
|
|
|||
|
|
`email-connect` is the reference adapter for the `coordination-engine` adapter layer. It provides email-specific sending, event ingestion, event normalization, evidence classification, message timelines, endpoint state, and provider abstraction.
|
|||
|
|
|
|||
|
|
The central design objective is to make email useful as a notification and communication channel without overclaiming what email can prove.
|
|||
|
|
|
|||
|
|
Email can often provide evidence that:
|
|||
|
|
|
|||
|
|
* a message was accepted by an outbound provider
|
|||
|
|
* a message was accepted by a recipient mail server
|
|||
|
|
* a message was rejected, bounced, deferred, delayed, dropped, or suppressed
|
|||
|
|
* a user agent, proxy, scanner, or recipient-like actor interacted with tracking mechanisms
|
|||
|
|
* a recipient or mailbox system replied, complained, unsubscribed, or emitted an out-of-office response
|
|||
|
|
|
|||
|
|
Email usually cannot prove by itself that:
|
|||
|
|
|
|||
|
|
* the message reached the recipient’s inbox
|
|||
|
|
* the intended human recipient saw the message
|
|||
|
|
* the recipient understood the message
|
|||
|
|
* the intended recipient clicked a link
|
|||
|
|
* the recipient accessed the primary payload
|
|||
|
|
* the coordination case result was achieved
|
|||
|
|
|
|||
|
|
Therefore, `email-connect` reports email evidence. `coordination-engine` decides what that evidence means for a coordination case.
|
|||
|
|
|
|||
|
|
## 3. Core Principle
|
|||
|
|
|
|||
|
|
Email is a weak-to-medium evidence channel for awareness. It is not a reliable proof channel for payload delivery or intended-recipient action.
|
|||
|
|
|
|||
|
|
The adapter MUST distinguish:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
technical email transport evidence
|
|||
|
|
mailbox or endpoint inference
|
|||
|
|
engagement evidence
|
|||
|
|
identity-bound interaction evidence from other systems
|
|||
|
|
coordination result evidence
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
In the coordination model, most email events map to `undef` or weak evidence unless paired with stronger evidence from another adapter such as `portal-connect`, `identity-connect`, `signature-connect`, or `payment-connect`.
|
|||
|
|
|
|||
|
|
## 4. Architectural Role
|
|||
|
|
|
|||
|
|
### 4.1 Standalone Role
|
|||
|
|
|
|||
|
|
As a standalone component, `email-connect` provides:
|
|||
|
|
|
|||
|
|
* provider-neutral email sending
|
|||
|
|
* transactional email dispatch
|
|||
|
|
* provider event ingestion
|
|||
|
|
* bounce classification
|
|||
|
|
* complaint and unsubscribe handling
|
|||
|
|
* suppression list management
|
|||
|
|
* open and click tracking interpretation
|
|||
|
|
* recipient endpoint diagnostics
|
|||
|
|
* email message timelines
|
|||
|
|
* deliverability diagnostics
|
|||
|
|
* normalized email event stream
|
|||
|
|
* evidence-oriented message assessment
|
|||
|
|
|
|||
|
|
### 4.2 coordination-engine Adapter Role
|
|||
|
|
|
|||
|
|
As a `coordination-engine` adapter, `email-connect` provides:
|
|||
|
|
|
|||
|
|
#### Actions
|
|||
|
|
|
|||
|
|
* `notification.send`
|
|||
|
|
* `notification.send_reminder`
|
|||
|
|
* `notification.register_tracking_context`
|
|||
|
|
* `recipient.suppress`
|
|||
|
|
* `recipient.unsuppress`
|
|||
|
|
* optionally `notification.cancel` where supported by queue state
|
|||
|
|
* optionally `notification.schedule` where supported
|
|||
|
|
|
|||
|
|
#### Signals
|
|||
|
|
|
|||
|
|
* `notification.attempt.accepted_by_adapter`
|
|||
|
|
* `notification.attempt.rejected_by_adapter`
|
|||
|
|
* `notification.attempt.accepted_by_provider`
|
|||
|
|
* `notification.attempt.rejected_by_provider`
|
|||
|
|
* `notification.attempt.queued`
|
|||
|
|
* `notification.attempt.delayed`
|
|||
|
|
* `notification.endpoint.accepted`
|
|||
|
|
* `notification.endpoint.deferred`
|
|||
|
|
* `notification.endpoint.rejected_temporary`
|
|||
|
|
* `notification.endpoint.rejected_permanent`
|
|||
|
|
* `notification.endpoint.unreachable`
|
|||
|
|
* `notification.channel.complaint_received`
|
|||
|
|
* `notification.channel.unsubscribe_received`
|
|||
|
|
* `notification.channel.suppression_added`
|
|||
|
|
* `notification.channel.reputation_warning`
|
|||
|
|
* `interaction.notification.opened`
|
|||
|
|
* `interaction.proxy_or_privacy_interaction`
|
|||
|
|
* `interaction.link.clicked`
|
|||
|
|
* `interaction.scanner_or_bot_interaction`
|
|||
|
|
* `interaction.unverified_actor_interaction`
|
|||
|
|
* `interaction.reply_received`
|
|||
|
|
* `interaction.out_of_office_received`
|
|||
|
|
|
|||
|
|
## 5. Relationship to coordination-engine
|
|||
|
|
|
|||
|
|
`email-connect` does not own:
|
|||
|
|
|
|||
|
|
* `CoordinationCase`
|
|||
|
|
* intended result evaluation
|
|||
|
|
* participant-level success
|
|||
|
|
* case-level success
|
|||
|
|
* legal delivery interpretation
|
|||
|
|
* portal access
|
|||
|
|
* payload retrieval
|
|||
|
|
* contract acceptance
|
|||
|
|
* payment settlement
|
|||
|
|
* signature completion
|
|||
|
|
* multi-channel escalation policy
|
|||
|
|
|
|||
|
|
`email-connect` owns:
|
|||
|
|
|
|||
|
|
* email send requests
|
|||
|
|
* email provider abstraction
|
|||
|
|
* email provider identifiers
|
|||
|
|
* email event ingestion
|
|||
|
|
* email event classification
|
|||
|
|
* email-native status timeline
|
|||
|
|
* email endpoint diagnostics
|
|||
|
|
* email evidence mapping
|
|||
|
|
* suppression and complaint handling
|
|||
|
|
* email-specific uncertainty classification
|
|||
|
|
|
|||
|
|
The boundary rule is:
|
|||
|
|
|
|||
|
|
> email-connect reports what happened in the email channel and what that may indicate. coordination-engine decides what that means for the coordination case.
|
|||
|
|
|
|||
|
|
## 6. Email as Notification, Not Delivery
|
|||
|
|
|
|||
|
|
Within `coordination-engine`, email is primarily a **notification channel**.
|
|||
|
|
|
|||
|
|
Email may contain small inline payloads, but in the primary architecture it usually points participants toward an action surface such as:
|
|||
|
|
|
|||
|
|
* a portal page
|
|||
|
|
* a secure document download
|
|||
|
|
* a payment page
|
|||
|
|
* a contract signing flow
|
|||
|
|
* an approval screen
|
|||
|
|
* a form submission page
|
|||
|
|
|
|||
|
|
The actual payload delivery or result-relevant interaction should usually be observed by another adapter.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
email-connect:
|
|||
|
|
notification.endpoint.accepted
|
|||
|
|
interaction.link.clicked
|
|||
|
|
|
|||
|
|
portal-connect:
|
|||
|
|
identity.actor_authenticated
|
|||
|
|
delivery.payload.downloaded
|
|||
|
|
|
|||
|
|
coordination-engine:
|
|||
|
|
participant result satisfied
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Email acceptance by the recipient mail server is not payload delivery. It is technical notification evidence.
|
|||
|
|
|
|||
|
|
## 7. Email Message Lifecycle Model
|
|||
|
|
|
|||
|
|
`email-connect` models an email notification as a lifecycle with multiple observable phases.
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
message.created
|
|||
|
|
message.rendered
|
|||
|
|
message.render_failed
|
|||
|
|
message.suppression_checked
|
|||
|
|
message.send_requested
|
|||
|
|
message.accepted_by_adapter
|
|||
|
|
message.rejected_by_adapter
|
|||
|
|
message.accepted_by_provider
|
|||
|
|
message.rejected_by_provider
|
|||
|
|
message.queued
|
|||
|
|
message.provider_dropped
|
|||
|
|
message.provider_suppressed
|
|||
|
|
message.deferred
|
|||
|
|
message.endpoint_accepted
|
|||
|
|
message.soft_bounced
|
|||
|
|
message.hard_bounced
|
|||
|
|
message.async_bounced
|
|||
|
|
message.delayed
|
|||
|
|
message.expired_without_delivery
|
|||
|
|
message.opened
|
|||
|
|
message.opened_proxy_like
|
|||
|
|
message.clicked
|
|||
|
|
message.clicked_scanner_like
|
|||
|
|
message.clicked_unverified
|
|||
|
|
message.replied
|
|||
|
|
message.out_of_office_received
|
|||
|
|
message.complaint_received
|
|||
|
|
message.unsubscribe_received
|
|||
|
|
message.suppression_added
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The lifecycle is not strictly linear. Events may arrive late, out of order, duplicated, or in conflict.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
provider accepted
|
|||
|
|
endpoint accepted
|
|||
|
|
later async bounce received
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The adapter MUST preserve all meaningful events and avoid collapsing the message into a single final status too early.
|
|||
|
|
|
|||
|
|
## 8. Email Attempt vs Email Message vs Recipient
|
|||
|
|
|
|||
|
|
The adapter MUST distinguish at least three layers.
|
|||
|
|
|
|||
|
|
### 8.1 EmailMessage
|
|||
|
|
|
|||
|
|
The logical message created by a client or coordination case.
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
EmailMessage:
|
|||
|
|
email_message_id: string
|
|||
|
|
coordination_case_id: string?
|
|||
|
|
participant_id: string?
|
|||
|
|
purpose: string?
|
|||
|
|
subject: string?
|
|||
|
|
template_ref: string?
|
|||
|
|
content_ref: string?
|
|||
|
|
tracking_context: TrackingContext
|
|||
|
|
created_at: timestamp
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 8.2 EmailAttempt
|
|||
|
|
|
|||
|
|
One send attempt for one message through one provider/configuration.
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
EmailAttempt:
|
|||
|
|
email_attempt_id: string
|
|||
|
|
email_message_id: string
|
|||
|
|
provider_name: string
|
|||
|
|
provider_account_ref: string?
|
|||
|
|
from_endpoint: EmailEndpoint
|
|||
|
|
to_endpoint: EmailEndpoint
|
|||
|
|
cc_endpoints:
|
|||
|
|
- EmailEndpoint
|
|||
|
|
bcc_endpoints:
|
|||
|
|
- EmailEndpoint
|
|||
|
|
provider_message_id: string?
|
|||
|
|
adapter_operation_id: string?
|
|||
|
|
state: EmailAttemptState
|
|||
|
|
created_at: timestamp
|
|||
|
|
updated_at: timestamp
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 8.3 EmailEndpoint
|
|||
|
|
|
|||
|
|
The target email endpoint.
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
EmailEndpoint:
|
|||
|
|
endpoint_id: string?
|
|||
|
|
email_address: string
|
|||
|
|
display_name: string?
|
|||
|
|
endpoint_role: from | to | cc | bcc | reply_to | return_path
|
|||
|
|
verification_state: unknown | syntax_validated | verified | suspected_invalid | invalid
|
|||
|
|
suppression_state: active | suppressed | complaint_suppressed | bounce_suppressed | unsubscribed | unknown
|
|||
|
|
metadata: object?
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Per-recipient modeling is required because one email message may have multiple recipients and different outcomes per recipient.
|
|||
|
|
|
|||
|
|
For coordination-engine integration, the preferred mode is one logical notification per participant endpoint.
|
|||
|
|
|
|||
|
|
## 9. Email Attempt States
|
|||
|
|
|
|||
|
|
The adapter SHOULD support these attempt states:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
created
|
|||
|
|
rendered
|
|||
|
|
render_failed
|
|||
|
|
suppressed
|
|||
|
|
send_requested
|
|||
|
|
accepted_by_adapter
|
|||
|
|
rejected_by_adapter
|
|||
|
|
accepted_by_provider
|
|||
|
|
rejected_by_provider
|
|||
|
|
queued
|
|||
|
|
scheduled
|
|||
|
|
provider_dropped
|
|||
|
|
provider_suppressed
|
|||
|
|
deferred
|
|||
|
|
endpoint_accepted
|
|||
|
|
soft_bounced
|
|||
|
|
hard_bounced
|
|||
|
|
async_bounced
|
|||
|
|
delayed
|
|||
|
|
expired_without_delivery
|
|||
|
|
complaint_received
|
|||
|
|
unsubscribe_received
|
|||
|
|
unknown
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
These states are email-native. They are not coordination result states.
|
|||
|
|
|
|||
|
|
## 10. Email Evidence Assessment
|
|||
|
|
|
|||
|
|
`email-connect` should provide an email-native assessment separate from coordination-engine state.
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
EmailEvidenceAssessment:
|
|||
|
|
email_message_id: string
|
|||
|
|
participant_id: string?
|
|||
|
|
category: success | fail | undef
|
|||
|
|
subclass: string
|
|||
|
|
confidence: low | medium | high
|
|||
|
|
strongest_signal: string?
|
|||
|
|
evidence_summary:
|
|||
|
|
- string
|
|||
|
|
recommended_coordination_interpretation: string?
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The assessment categories are adapter-level hints.
|
|||
|
|
|
|||
|
|
### 10.1 Email Adapter Success
|
|||
|
|
|
|||
|
|
Email-level `success` means only that the email channel produced strong email-channel evidence.
|
|||
|
|
|
|||
|
|
Possible subclasses:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
success.endpoint_accepted
|
|||
|
|
success.reply_received
|
|||
|
|
success.human_like_open
|
|||
|
|
success.human_like_click
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Important: These are not automatically coordination success.
|
|||
|
|
|
|||
|
|
`success.endpoint_accepted` is technical success for the email transport, but usually maps to coordination `undef.technical_acceptance_only`.
|
|||
|
|
|
|||
|
|
### 10.2 Email Adapter Fail
|
|||
|
|
|
|||
|
|
Email-level `fail` indicates strong evidence that the email channel failed or should not be used.
|
|||
|
|
|
|||
|
|
Subclasses:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
fail.invalid_address
|
|||
|
|
fail.missing_address
|
|||
|
|
fail.render_failed
|
|||
|
|
fail.template_invalid
|
|||
|
|
fail.provider_rejected
|
|||
|
|
fail.provider_dropped
|
|||
|
|
fail.provider_suppressed
|
|||
|
|
fail.hard_bounce
|
|||
|
|
fail.domain_not_found
|
|||
|
|
fail.mailbox_not_found
|
|||
|
|
fail.authentication_rejected
|
|||
|
|
fail.policy_rejected
|
|||
|
|
fail.content_rejected
|
|||
|
|
fail.message_too_large
|
|||
|
|
fail.complaint_received
|
|||
|
|
fail.unsubscribed
|
|||
|
|
fail.suppressed
|
|||
|
|
fail.expired_without_delivery
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 10.3 Email Adapter Undef
|
|||
|
|
|
|||
|
|
Email-level `undef` is the normal state when the adapter cannot infer reliable human awareness.
|
|||
|
|
|
|||
|
|
Subclasses:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
undef.pending
|
|||
|
|
undef.provider_accepted_only
|
|||
|
|
undef.queued
|
|||
|
|
undef.deferred
|
|||
|
|
undef.delayed
|
|||
|
|
undef.endpoint_accepted_only
|
|||
|
|
undef.no_signal
|
|||
|
|
undef.weak_positive
|
|||
|
|
undef.proxy_open
|
|||
|
|
undef.scanner_click
|
|||
|
|
undef.unverified_click
|
|||
|
|
undef.identity_uncertain
|
|||
|
|
undef.mailbox_possibly_reached
|
|||
|
|
undef.possibly_spam_or_quarantine
|
|||
|
|
undef.possibly_silent_drop
|
|||
|
|
undef.forwarding_unknown
|
|||
|
|
undef.catch_all_domain
|
|||
|
|
undef.conflicting_evidence
|
|||
|
|
undef.channel_degraded
|
|||
|
|
undef.recipient_reported_missing
|
|||
|
|
undef.out_of_office
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The `undef` category must not be empty because email does not provide guaranteed delivery or awareness evidence.
|
|||
|
|
|
|||
|
|
## 11. Detailed Email Scenario Classification
|
|||
|
|
|
|||
|
|
### 11.1 Pre-Send Scenarios
|
|||
|
|
|
|||
|
|
| Scenario | Email assessment | Normalized event | Notes |
|
|||
|
|
| -------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------- |
|
|||
|
|
| Missing address | `fail.missing_address` | `notification.attempt.rejected_by_adapter` | No send possible |
|
|||
|
|
| Invalid address syntax | `fail.invalid_address` | `notification.attempt.rejected_by_adapter` | Strong local failure |
|
|||
|
|
| Template rendering failure | `fail.render_failed` | `notification.attempt.rejected_by_adapter` or `payload.validation_failed` | Notification unusable |
|
|||
|
|
| Invalid generated link | `fail.template_invalid` | `notification.attempt.rejected_by_adapter` | Especially relevant for portal links |
|
|||
|
|
| Suppression hit | `fail.suppressed` | `notification.channel.suppression_added` or action rejected | May be due to bounce, complaint, unsubscribe |
|
|||
|
|
| Adapter config missing | `fail.provider_rejected` | `system.adapter.health_changed` + action error | Operational failure |
|
|||
|
|
| Provider unavailable before acceptance | `undef.pending` or `fail.provider_rejected` | action result error | Depends on retryability |
|
|||
|
|
| Submission timeout | `undef.pending` | action result unknown | Could have been accepted despite timeout |
|
|||
|
|
|
|||
|
|
### 11.2 Provider-Side Scenarios
|
|||
|
|
|
|||
|
|
| Scenario | Email assessment | Normalized event | Notes |
|
|||
|
|
| ----------------------------- | ------------------------------------------- | ------------------------------------------------------------------------ | -------------------------------- |
|
|||
|
|
| Adapter accepted request | `undef.pending` | `notification.attempt.accepted_by_adapter` | Adapter accepted work |
|
|||
|
|
| Provider accepted message | `undef.provider_accepted_only` | `notification.attempt.accepted_by_provider` | Sending began |
|
|||
|
|
| Provider rejected message | `fail.provider_rejected` | `notification.attempt.rejected_by_provider` | Strong failure |
|
|||
|
|
| Provider queued message | `undef.queued` | `notification.attempt.queued` | Pending |
|
|||
|
|
| Provider delayed message | `undef.delayed` | `notification.attempt.delayed` | Pending, may degrade |
|
|||
|
|
| Provider dropped message | `fail.provider_dropped` | `notification.attempt.rejected_by_provider` or dropped-specific metadata | Message may never leave provider |
|
|||
|
|
| Provider suppressed recipient | `fail.provider_suppressed` | `notification.channel.suppression_added` | Channel blocked |
|
|||
|
|
| Provider rate-limited | `undef.pending` | `notification.attempt.delayed` | Retry or wait |
|
|||
|
|
| Provider quota exceeded | `fail.provider_rejected` or `undef.pending` | action error | Policy-dependent |
|
|||
|
|
|
|||
|
|
### 11.3 Recipient Mail Server Scenarios
|
|||
|
|
|
|||
|
|
| Scenario | Email assessment | Normalized event | Notes |
|
|||
|
|
| ----------------------------- | --------------------------------------------------- | ------------------------------------------------------- | -------------------------------------- |
|
|||
|
|
| MX accepted | `undef.endpoint_accepted_only` | `notification.endpoint.accepted` | Does not prove inbox placement |
|
|||
|
|
| Temporary SMTP deferral | `undef.deferred` | `notification.endpoint.deferred` | Retry window active |
|
|||
|
|
| Soft bounce | `undef.deferred` or `fail.expired_without_delivery` | `notification.endpoint.rejected_temporary` | Becomes failure after retry policy |
|
|||
|
|
| Hard bounce unknown user | `fail.mailbox_not_found` | `notification.endpoint.rejected_permanent` | Strong endpoint failure |
|
|||
|
|
| Domain not found | `fail.domain_not_found` | `notification.endpoint.unreachable` | Strong failure |
|
|||
|
|
| SPF/DKIM/DMARC rejection | `fail.authentication_rejected` | `notification.endpoint.rejected_permanent` | Strong sender config/channel failure |
|
|||
|
|
| Content policy rejection | `fail.content_rejected` | `notification.endpoint.rejected_permanent` | Strong failure |
|
|||
|
|
| Recipient mailbox full | `undef.deferred` | `notification.endpoint.rejected_temporary` | May later succeed |
|
|||
|
|
| Greylisting | `undef.deferred` | `notification.endpoint.deferred` | Usually retryable |
|
|||
|
|
| Async bounce after acceptance | `fail.async_bounced` | `notification.endpoint.rejected_permanent` or temporary | Important late failure |
|
|||
|
|
| Catch-all domain acceptance | `undef.catch_all_domain` | `notification.endpoint.accepted` + metadata | Acceptance is especially weak |
|
|||
|
|
| Forwarding unknown | `undef.forwarding_unknown` | `notification.endpoint.accepted` + metadata if known | Intended recipient awareness uncertain |
|
|||
|
|
|
|||
|
|
### 11.4 Mailbox and Inbox Inference Scenarios
|
|||
|
|
|
|||
|
|
| Scenario | Email assessment | Notes |
|
|||
|
|
| --------------------------------- | -------------------------------------------------------- | ----- |
|
|||
|
|
| Delivered to inbox | Usually not directly observable | |
|
|||
|
|
| Delivered to spam/junk | Usually not directly observable | |
|
|||
|
|
| Delivered to promotions/other tab | Usually not directly observable | |
|
|||
|
|
| Delivered then moved by user rule | Usually not directly observable | |
|
|||
|
|
| Delivered then silently dropped | Usually not directly observable | |
|
|||
|
|
| Quarantined by enterprise gateway | Usually not directly observable unless feedback received | |
|
|||
|
|
| Accepted by shared mailbox | Awareness of intended person uncertain | |
|
|||
|
|
| Accepted by inactive mailbox | Endpoint works, awareness uncertain | |
|
|||
|
|
| Out-of-office reply | Mailbox likely reached, but action uncertain | |
|
|||
|
|
| User reports missing email | Conflicting evidence; treat as suspicious | |
|
|||
|
|
| User reports spam-folder location | Useful diagnostic; still not initial success | |
|
|||
|
|
|
|||
|
|
These cases should usually map to `undef.*` because email lacks reliable inbox placement proof.
|
|||
|
|
|
|||
|
|
### 11.5 Open Tracking Scenarios
|
|||
|
|
|
|||
|
|
| Scenario | Email assessment | Normalized event | Notes |
|
|||
|
|
| ----------------------------- | -------------------------------------------------- | -------------------------------------------- | --------------------------------- |
|
|||
|
|
| No open | `undef.no_signal` | none | Absence of open proves nothing |
|
|||
|
|
| Pixel loaded normally | `undef.weak_positive` or `success.human_like_open` | `interaction.notification.opened` | Weak-to-medium |
|
|||
|
|
| Apple/privacy proxy-like open | `undef.proxy_open` | `interaction.proxy_or_privacy_interaction` | Not human proof |
|
|||
|
|
| Gmail/image proxy open | `undef.proxy_open` or weak positive | `interaction.proxy_or_privacy_interaction` | Ambiguous |
|
|||
|
|
| Security scanner image load | `undef.proxy_open` | `interaction.proxy_or_privacy_interaction` | Automated |
|
|||
|
|
| Multiple human-like opens | `success.human_like_open` | `interaction.notification.opened` | Still not payload delivery |
|
|||
|
|
| Open from unusual geography | `undef.identity_uncertain` | `interaction.notification.opened` + metadata | May be proxy, forwarding, or risk |
|
|||
|
|
|
|||
|
|
Open tracking MUST NOT be treated as strong coordination success.
|
|||
|
|
|
|||
|
|
### 11.6 Click Tracking Scenarios
|
|||
|
|
|
|||
|
|
| Scenario | Email assessment | Normalized event | Notes |
|
|||
|
|
| -------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------ | -------------------------------- |
|
|||
|
|
| No click | `undef.no_signal` | none | No evidence of interaction |
|
|||
|
|
| Scanner-like click | `undef.scanner_click` | `interaction.scanner_or_bot_interaction` | Do not count as recipient action |
|
|||
|
|
| Link prefetch | `undef.scanner_click` | `interaction.scanner_or_bot_interaction` | Common in enterprise security |
|
|||
|
|
| Unverified click | `undef.unverified_click` | `interaction.unverified_actor_interaction` | Identity uncertain |
|
|||
|
|
| Human-like click | `success.human_like_click` | `interaction.link.clicked` | Medium, still not identity-bound |
|
|||
|
|
| Click followed by authenticated portal login | email event remains weak; portal event is strong | Portal adapter provides identity evidence | |
|
|||
|
|
| Click followed by payload download | email contributed path evidence; delivery evidence is decisive | Delivery/portal event closes case | |
|
|||
|
|
| Token invalid after click | `fail.template_invalid` or `undef.identity_uncertain` | Depends on cause | |
|
|||
|
|
| Expired link clicked | `undef.weak_positive` | Awareness possible, action path failed | |
|
|||
|
|
| Forwarded link clicked | `undef.identity_uncertain` | Intended recipient unknown | |
|
|||
|
|
|
|||
|
|
The adapter SHOULD classify likely scanner clicks using timing, user-agent, IP ranges, known security vendors, HEAD/GET patterns, link fan-out, and immediate multi-link access where available.
|
|||
|
|
|
|||
|
|
### 11.7 Reply and Auto-Reply Scenarios
|
|||
|
|
|
|||
|
|
| Scenario | Email assessment | Normalized event | Notes |
|
|||
|
|
| ----------------------------------- | ---------------------------------------------- | --------------------------------------- | -------------------------------------------------------------- |
|
|||
|
|
| Human reply received | `success.reply_received` | `interaction.reply_received` | Strong awareness signal, identity still may require validation |
|
|||
|
|
| Out-of-office reply | `undef.out_of_office` | `interaction.out_of_office_received` | Mailbox reached, action uncertain |
|
|||
|
|
| Auto-reply from ticket system | `undef.identity_uncertain` | `interaction.reply_received` + metadata | System interaction |
|
|||
|
|
| Bounce-like auto-response | classify as bounce if parseable | notification endpoint event | Must parse carefully |
|
|||
|
|
| Reply from delegated/shared mailbox | `undef.identity_uncertain` or medium awareness | depends on participant model | |
|
|||
|
|
|
|||
|
|
### 11.8 Complaint, Unsubscribe, and Suppression
|
|||
|
|
|
|||
|
|
| Scenario | Email assessment | Normalized event | Notes |
|
|||
|
|
| --------------------------- | ------------------------- | ------------------------------------------- | ---------------------------------- |
|
|||
|
|
| Spam complaint | `fail.complaint_received` | `notification.channel.complaint_received` | Negative channel evidence |
|
|||
|
|
| Unsubscribe | `fail.unsubscribed` | `notification.channel.unsubscribe_received` | Future email use constrained |
|
|||
|
|
| Bounce suppression added | `fail.suppressed` | `notification.channel.suppression_added` | Channel blocked |
|
|||
|
|
| Complaint suppression added | `fail.suppressed` | `notification.channel.suppression_added` | Channel blocked |
|
|||
|
|
| Manual suppression | `fail.suppressed` | `notification.channel.suppression_added` | Operator action |
|
|||
|
|
| Suppression removed | active/unknown | `notification.channel.suppression_removed` | Channel may become available again |
|
|||
|
|
|
|||
|
|
For legally or operationally required notifications, unsubscribe semantics must be scenario-specific. `email-connect` records the event; `coordination-engine` decides whether email remains permissible.
|
|||
|
|
|
|||
|
|
## 12. Adapter-to-Coordination Mapping
|
|||
|
|
|
|||
|
|
### 12.1 Core Mapping Table
|
|||
|
|
|
|||
|
|
| Email-native event | Email assessment | coordination-engine event | Coordination interpretation |
|
|||
|
|
| ------------------ | ------------------------------------------- | ------------------------------------------------------- | --------------------------------- |
|
|||
|
|
| message created | `undef.pending` | `notification.attempt.created` | Attempt exists |
|
|||
|
|
| render failed | `fail.render_failed` | `notification.attempt.rejected_by_adapter` | Notification failed before send |
|
|||
|
|
| adapter accepted | `undef.pending` | `notification.attempt.accepted_by_adapter` | Work accepted |
|
|||
|
|
| adapter rejected | `fail.provider_rejected` or validation fail | `notification.attempt.rejected_by_adapter` | Attempt failed |
|
|||
|
|
| provider accepted | `undef.provider_accepted_only` | `notification.attempt.accepted_by_provider` | Weak send evidence |
|
|||
|
|
| provider rejected | `fail.provider_rejected` | `notification.attempt.rejected_by_provider` | Strong attempt failure |
|
|||
|
|
| provider queued | `undef.queued` | `notification.attempt.queued` | Pending |
|
|||
|
|
| provider delayed | `undef.delayed` | `notification.attempt.delayed` | Pending/degraded |
|
|||
|
|
| provider dropped | `fail.provider_dropped` | `notification.attempt.rejected_by_provider` | Strong failure |
|
|||
|
|
| MX accepted | `undef.endpoint_accepted_only` | `notification.endpoint.accepted` | Technical delivery only |
|
|||
|
|
| temporary deferral | `undef.deferred` | `notification.endpoint.deferred` | Pending |
|
|||
|
|
| soft bounce | `undef.deferred` | `notification.endpoint.rejected_temporary` | Retryable/pending |
|
|||
|
|
| hard bounce | `fail.hard_bounce` | `notification.endpoint.rejected_permanent` | Strong failure |
|
|||
|
|
| async bounce | `fail.async_bounced` | `notification.endpoint.rejected_permanent` or temporary | Late failure |
|
|||
|
|
| open human-like | `success.human_like_open` | `interaction.notification.opened` | Weak-to-medium awareness |
|
|||
|
|
| open proxy-like | `undef.proxy_open` | `interaction.proxy_or_privacy_interaction` | Ambiguous |
|
|||
|
|
| click scanner-like | `undef.scanner_click` | `interaction.scanner_or_bot_interaction` | Ambiguous/automated |
|
|||
|
|
| click unverified | `undef.unverified_click` | `interaction.unverified_actor_interaction` | Identity uncertain |
|
|||
|
|
| reply received | `success.reply_received` | `interaction.reply_received` | Medium-to-strong awareness |
|
|||
|
|
| OOO received | `undef.out_of_office` | `interaction.out_of_office_received` | Mailbox reached, action uncertain |
|
|||
|
|
| complaint | `fail.complaint_received` | `notification.channel.complaint_received` | Strong negative channel signal |
|
|||
|
|
| unsubscribe | `fail.unsubscribed` | `notification.channel.unsubscribe_received` | Future channel constraint |
|
|||
|
|
| suppression added | `fail.suppressed` | `notification.channel.suppression_added` | Channel blocked |
|
|||
|
|
|
|||
|
|
### 12.2 Coordination Undef Subclasses
|
|||
|
|
|
|||
|
|
`coordination-engine` may derive these participant uncertainty classes from email evidence:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
undef.pending
|
|||
|
|
undef.technical_acceptance_only
|
|||
|
|
undef.no_signal
|
|||
|
|
undef.weak_positive
|
|||
|
|
undef.identity_uncertain
|
|||
|
|
undef.channel_suspicious
|
|||
|
|
undef.conflicting_evidence
|
|||
|
|
undef.delivery_pending
|
|||
|
|
undef.escalation_required
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Email evidence should commonly produce:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
undef.pending
|
|||
|
|
undef.technical_acceptance_only
|
|||
|
|
undef.weak_positive
|
|||
|
|
undef.identity_uncertain
|
|||
|
|
undef.channel_suspicious
|
|||
|
|
undef.conflicting_evidence
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 13. Evidence Grading Rules
|
|||
|
|
|
|||
|
|
### 13.1 Provider Acceptance
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
event_type: notification.attempt.accepted_by_provider
|
|||
|
|
evidence_grade:
|
|||
|
|
strength: weak
|
|||
|
|
actor_certainty: none
|
|||
|
|
authority_certainty: none
|
|||
|
|
payload_certainty: low
|
|||
|
|
interaction_certainty: none
|
|||
|
|
timing_certainty: medium
|
|||
|
|
channel_certainty: medium
|
|||
|
|
non_repudiation_strength: none
|
|||
|
|
notes:
|
|||
|
|
- Provider accepted message for processing.
|
|||
|
|
- Does not prove recipient endpoint acceptance.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 13.2 Recipient MX Acceptance
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
event_type: notification.endpoint.accepted
|
|||
|
|
evidence_grade:
|
|||
|
|
strength: weak
|
|||
|
|
actor_certainty: none
|
|||
|
|
authority_certainty: none
|
|||
|
|
payload_certainty: low
|
|||
|
|
interaction_certainty: none
|
|||
|
|
timing_certainty: medium
|
|||
|
|
channel_certainty: medium
|
|||
|
|
non_repudiation_strength: none
|
|||
|
|
notes:
|
|||
|
|
- Recipient mail server accepted the message.
|
|||
|
|
- Does not prove inbox placement or human awareness.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 13.3 Hard Bounce
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
event_type: notification.endpoint.rejected_permanent
|
|||
|
|
evidence_grade:
|
|||
|
|
strength: negative
|
|||
|
|
actor_certainty: none
|
|||
|
|
authority_certainty: none
|
|||
|
|
payload_certainty: none
|
|||
|
|
interaction_certainty: none
|
|||
|
|
timing_certainty: medium
|
|||
|
|
channel_certainty: high
|
|||
|
|
non_repudiation_strength: low
|
|||
|
|
notes:
|
|||
|
|
- Strong evidence the email endpoint is not usable for this attempt.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 13.4 Soft Bounce / Deferral
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
event_type: notification.endpoint.rejected_temporary
|
|||
|
|
evidence_grade:
|
|||
|
|
strength: ambiguous
|
|||
|
|
actor_certainty: none
|
|||
|
|
authority_certainty: none
|
|||
|
|
payload_certainty: none
|
|||
|
|
interaction_certainty: none
|
|||
|
|
timing_certainty: medium
|
|||
|
|
channel_certainty: medium
|
|||
|
|
non_repudiation_strength: none
|
|||
|
|
notes:
|
|||
|
|
- Temporary failure or deferral.
|
|||
|
|
- May later resolve or expire.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 13.5 Proxy Open
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
event_type: interaction.proxy_or_privacy_interaction
|
|||
|
|
evidence_grade:
|
|||
|
|
strength: ambiguous
|
|||
|
|
actor_certainty: low
|
|||
|
|
authority_certainty: none
|
|||
|
|
payload_certainty: low
|
|||
|
|
interaction_certainty: low
|
|||
|
|
timing_certainty: low
|
|||
|
|
channel_certainty: medium
|
|||
|
|
non_repudiation_strength: none
|
|||
|
|
notes:
|
|||
|
|
- Open may be caused by privacy proxy, image proxy, or automated prefetch.
|
|||
|
|
- Should not be treated as human awareness in high-assurance scenarios.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 13.6 Scanner-Like Click
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
event_type: interaction.scanner_or_bot_interaction
|
|||
|
|
evidence_grade:
|
|||
|
|
strength: ambiguous
|
|||
|
|
actor_certainty: low
|
|||
|
|
authority_certainty: none
|
|||
|
|
payload_certainty: low
|
|||
|
|
interaction_certainty: low
|
|||
|
|
timing_certainty: medium
|
|||
|
|
channel_certainty: medium
|
|||
|
|
non_repudiation_strength: none
|
|||
|
|
notes:
|
|||
|
|
- Click appears automated.
|
|||
|
|
- Should not be treated as recipient engagement.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 13.7 Unverified Click
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
event_type: interaction.unverified_actor_interaction
|
|||
|
|
evidence_grade:
|
|||
|
|
strength: medium
|
|||
|
|
actor_certainty: low
|
|||
|
|
authority_certainty: none
|
|||
|
|
payload_certainty: medium
|
|||
|
|
interaction_certainty: medium
|
|||
|
|
timing_certainty: medium
|
|||
|
|
channel_certainty: medium
|
|||
|
|
non_repudiation_strength: none
|
|||
|
|
notes:
|
|||
|
|
- Link was clicked, but actor identity is not proven.
|
|||
|
|
- May indicate awareness but should be confirmed by portal or identity evidence.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 13.8 Human Reply
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
event_type: interaction.reply_received
|
|||
|
|
evidence_grade:
|
|||
|
|
strength: strong
|
|||
|
|
actor_certainty: medium
|
|||
|
|
authority_certainty: low
|
|||
|
|
payload_certainty: medium
|
|||
|
|
interaction_certainty: high
|
|||
|
|
timing_certainty: high
|
|||
|
|
channel_certainty: high
|
|||
|
|
non_repudiation_strength: low
|
|||
|
|
notes:
|
|||
|
|
- Reply indicates mailbox-level interaction.
|
|||
|
|
- Identity and authority may still require validation.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 13.9 Complaint
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
event_type: notification.channel.complaint_received
|
|||
|
|
evidence_grade:
|
|||
|
|
strength: negative
|
|||
|
|
actor_certainty: medium
|
|||
|
|
authority_certainty: none
|
|||
|
|
payload_certainty: low
|
|||
|
|
interaction_certainty: high
|
|||
|
|
timing_certainty: high
|
|||
|
|
channel_certainty: high
|
|||
|
|
non_repudiation_strength: low
|
|||
|
|
notes:
|
|||
|
|
- Complaint is strong negative channel evidence.
|
|||
|
|
- Future use of the channel may be constrained.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 14. Message Timeline API
|
|||
|
|
|
|||
|
|
`email-connect` SHOULD expose a message timeline suitable for standalone diagnostics and coordination audit.
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
EmailMessageTimeline:
|
|||
|
|
email_message_id: string
|
|||
|
|
email_attempts:
|
|||
|
|
- email_attempt_id: string
|
|||
|
|
provider_name: string
|
|||
|
|
provider_message_id: string?
|
|||
|
|
events:
|
|||
|
|
- EmailTimelineEvent
|
|||
|
|
current_assessment: EmailEvidenceAssessment
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
EmailTimelineEvent:
|
|||
|
|
event_id: string
|
|||
|
|
event_type: string
|
|||
|
|
occurred_at: timestamp
|
|||
|
|
source: adapter | provider | inbound_mail | tracking | operator
|
|||
|
|
native_event_type: string?
|
|||
|
|
normalized_event_type: string?
|
|||
|
|
summary: string
|
|||
|
|
evidence_grade: EvidenceGrade
|
|||
|
|
raw_event_ref: string?
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 15. Adapter Descriptor
|
|||
|
|
|
|||
|
|
`email-connect` MUST expose an `AdapterDescriptor` compatible with AdapterInterfaceSpecification.md v1.0.
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
adapter_id: email-connect.default
|
|||
|
|
adapter_name: email-connect
|
|||
|
|
adapter_version: 1.0.0
|
|||
|
|
adapter_contract_version: 1.0
|
|||
|
|
adapter_types:
|
|||
|
|
- notification
|
|||
|
|
- communication
|
|||
|
|
- interaction
|
|||
|
|
provider_family: email
|
|||
|
|
provider_name: configurable
|
|||
|
|
deployment_mode: external
|
|||
|
|
supported_channels:
|
|||
|
|
- email
|
|||
|
|
supported_endpoint_types:
|
|||
|
|
- email_address
|
|||
|
|
supported_actions:
|
|||
|
|
- action_type: notification.send
|
|||
|
|
mode: async
|
|||
|
|
idempotency_required: true
|
|||
|
|
- action_type: notification.send_reminder
|
|||
|
|
mode: async
|
|||
|
|
idempotency_required: true
|
|||
|
|
- action_type: notification.register_tracking_context
|
|||
|
|
mode: sync
|
|||
|
|
idempotency_required: true
|
|||
|
|
- action_type: recipient.suppress
|
|||
|
|
mode: sync
|
|||
|
|
idempotency_required: true
|
|||
|
|
- action_type: recipient.unsuppress
|
|||
|
|
mode: sync
|
|||
|
|
idempotency_required: true
|
|||
|
|
emitted_event_types:
|
|||
|
|
- notification.attempt.created
|
|||
|
|
- notification.attempt.accepted_by_adapter
|
|||
|
|
- notification.attempt.rejected_by_adapter
|
|||
|
|
- notification.attempt.accepted_by_provider
|
|||
|
|
- notification.attempt.rejected_by_provider
|
|||
|
|
- notification.attempt.queued
|
|||
|
|
- notification.attempt.delayed
|
|||
|
|
- notification.endpoint.accepted
|
|||
|
|
- notification.endpoint.deferred
|
|||
|
|
- notification.endpoint.rejected_temporary
|
|||
|
|
- notification.endpoint.rejected_permanent
|
|||
|
|
- notification.endpoint.unreachable
|
|||
|
|
- notification.channel.complaint_received
|
|||
|
|
- notification.channel.unsubscribe_received
|
|||
|
|
- notification.channel.suppression_added
|
|||
|
|
- notification.channel.suppression_removed
|
|||
|
|
- notification.channel.reputation_warning
|
|||
|
|
- interaction.notification.opened
|
|||
|
|
- interaction.proxy_or_privacy_interaction
|
|||
|
|
- interaction.link.clicked
|
|||
|
|
- interaction.scanner_or_bot_interaction
|
|||
|
|
- interaction.unverified_actor_interaction
|
|||
|
|
- interaction.reply_received
|
|||
|
|
- interaction.out_of_office_received
|
|||
|
|
evidence_profile:
|
|||
|
|
strongest_evidence_level: weak_to_medium
|
|||
|
|
can_prove_human_awareness: false
|
|||
|
|
can_prove_payload_delivery: false
|
|||
|
|
can_prove_identity: false
|
|||
|
|
identity_profile:
|
|||
|
|
identity_strength: none
|
|||
|
|
authority_strength: none
|
|||
|
|
limitations:
|
|||
|
|
- Recipient mail server acceptance does not prove inbox delivery.
|
|||
|
|
- Absence of bounce does not prove delivery.
|
|||
|
|
- Open tracking is ambiguous.
|
|||
|
|
- Click tracking can be caused by security scanners.
|
|||
|
|
- Email events alone usually cannot prove intended-recipient awareness.
|
|||
|
|
- Email events do not prove payload access unless the payload is embedded and policy accepts that as sufficient.
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 16. Action Request Handling
|
|||
|
|
|
|||
|
|
### 16.1 `notification.send`
|
|||
|
|
|
|||
|
|
`notification.send` sends a new email notification.
|
|||
|
|
|
|||
|
|
Required fields:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
request_id
|
|||
|
|
action_type
|
|||
|
|
coordination_case_id
|
|||
|
|
participant_id
|
|||
|
|
target_endpoint
|
|||
|
|
content or template_ref
|
|||
|
|
tracking_context
|
|||
|
|
idempotency_key
|
|||
|
|
requested_at
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
request_id: req_001
|
|||
|
|
action_type: notification.send
|
|||
|
|
coordination_case_id: case_123
|
|||
|
|
participant_id: participant_456
|
|||
|
|
channel: email
|
|||
|
|
target_endpoint:
|
|||
|
|
endpoint_type: email_address
|
|||
|
|
value: recipient@example.com
|
|||
|
|
template_ref: secure-document-available
|
|||
|
|
variables:
|
|||
|
|
recipient_name: Example Recipient
|
|||
|
|
portal_link: https://portal.example.com/access/abc
|
|||
|
|
tracking_context:
|
|||
|
|
correlation_id: corr_789
|
|||
|
|
coordination_case_id: case_123
|
|||
|
|
participant_id: participant_456
|
|||
|
|
notification_id: notif_001
|
|||
|
|
payload_id: payload_777
|
|||
|
|
idempotency_key: case_123:participant_456:notif_001
|
|||
|
|
requested_at: 2026-01-01T12:00:00Z
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 16.2 Action Result
|
|||
|
|
|
|||
|
|
The adapter returns:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
request_id: req_001
|
|||
|
|
adapter_id: email-connect.default
|
|||
|
|
accepted: true
|
|||
|
|
action_state: accepted
|
|||
|
|
adapter_operation_id: emailop_001
|
|||
|
|
provider_operation_id: providerop_001
|
|||
|
|
initial_events:
|
|||
|
|
- event_type: notification.attempt.accepted_by_adapter
|
|||
|
|
received_at: 2026-01-01T12:00:01Z
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The result does not prove the email was delivered or seen.
|
|||
|
|
|
|||
|
|
## 17. Provider Abstraction
|
|||
|
|
|
|||
|
|
`email-connect` SHOULD support a provider abstraction layer.
|
|||
|
|
|
|||
|
|
Provider integration responsibilities:
|
|||
|
|
|
|||
|
|
* send messages
|
|||
|
|
* store provider message IDs
|
|||
|
|
* verify webhooks
|
|||
|
|
* ingest provider events
|
|||
|
|
* normalize provider event names
|
|||
|
|
* parse bounce reasons
|
|||
|
|
* parse complaints and unsubscribes
|
|||
|
|
* handle provider-specific suppression events
|
|||
|
|
* expose provider health
|
|||
|
|
|
|||
|
|
Provider model:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
EmailProvider:
|
|||
|
|
provider_name: string
|
|||
|
|
provider_account_ref: string
|
|||
|
|
supported_features:
|
|||
|
|
- sending
|
|||
|
|
- webhooks
|
|||
|
|
- bounce_events
|
|||
|
|
- open_tracking
|
|||
|
|
- click_tracking
|
|||
|
|
- suppression_api
|
|||
|
|
- template_rendering
|
|||
|
|
event_mapping_ref: string
|
|||
|
|
configuration_ref: string
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The first implementation MAY use a simulated provider. Real providers SHOULD be added behind this abstraction.
|
|||
|
|
|
|||
|
|
## 18. Native Provider Event Mapping
|
|||
|
|
|
|||
|
|
The adapter MUST support provider-specific mapping files or code modules.
|
|||
|
|
|
|||
|
|
Examples of provider-native event groups:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
accepted
|
|||
|
|
processed
|
|||
|
|
queued
|
|||
|
|
delivered
|
|||
|
|
deferred
|
|||
|
|
delayed
|
|||
|
|
bounce
|
|||
|
|
dropped
|
|||
|
|
failed
|
|||
|
|
rejected
|
|||
|
|
open
|
|||
|
|
click
|
|||
|
|
spam_report
|
|||
|
|
unsubscribe
|
|||
|
|
suppressed
|
|||
|
|
rendering_failed
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Important mapping rule:
|
|||
|
|
|
|||
|
|
Provider `delivered` events MUST be interpreted carefully.
|
|||
|
|
|
|||
|
|
For many providers, `delivered` means the receiving mail server accepted the email. It MUST map to:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
notification.endpoint.accepted
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
not to coordination success.
|
|||
|
|
|
|||
|
|
## 19. Bounce Classification
|
|||
|
|
|
|||
|
|
`email-connect` SHOULD classify bounces into structured reasons.
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
EmailBounce:
|
|||
|
|
bounce_type: hard | soft | transient | unknown
|
|||
|
|
reason_code: string
|
|||
|
|
enhanced_status_code: string?
|
|||
|
|
smtp_status_code: string?
|
|||
|
|
diagnostic_text: string?
|
|||
|
|
retryable: boolean
|
|||
|
|
classification:
|
|||
|
|
- mailbox_not_found
|
|||
|
|
- domain_not_found
|
|||
|
|
- mailbox_full
|
|||
|
|
- message_too_large
|
|||
|
|
- authentication_failed
|
|||
|
|
- policy_rejected
|
|||
|
|
- content_rejected
|
|||
|
|
- reputation_rejected
|
|||
|
|
- rate_limited
|
|||
|
|
- greylisted
|
|||
|
|
- temporary_server_failure
|
|||
|
|
- suppressed
|
|||
|
|
- unknown
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Suggested mappings:
|
|||
|
|
|
|||
|
|
| Bounce class | Email assessment | Normalized event |
|
|||
|
|
| ------------------------ | ------------------------------ | -------------------------------------------------------- |
|
|||
|
|
| mailbox_not_found | `fail.mailbox_not_found` | `notification.endpoint.rejected_permanent` |
|
|||
|
|
| domain_not_found | `fail.domain_not_found` | `notification.endpoint.unreachable` |
|
|||
|
|
| mailbox_full | `undef.deferred` | `notification.endpoint.rejected_temporary` |
|
|||
|
|
| authentication_failed | `fail.authentication_rejected` | `notification.endpoint.rejected_permanent` |
|
|||
|
|
| policy_rejected | `fail.policy_rejected` | `notification.endpoint.rejected_permanent` |
|
|||
|
|
| content_rejected | `fail.content_rejected` | `notification.endpoint.rejected_permanent` |
|
|||
|
|
| reputation_rejected | `fail.policy_rejected` | `notification.channel.reputation_warning` plus rejection |
|
|||
|
|
| greylisted | `undef.deferred` | `notification.endpoint.deferred` |
|
|||
|
|
| temporary_server_failure | `undef.deferred` | `notification.endpoint.rejected_temporary` |
|
|||
|
|
|
|||
|
|
## 20. Suppression Model
|
|||
|
|
|
|||
|
|
`email-connect` SHOULD maintain or integrate with a suppression model.
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
EmailSuppression:
|
|||
|
|
endpoint_ref: EndpointRef
|
|||
|
|
suppression_type: hard_bounce | complaint | unsubscribe | manual | provider_policy | unknown
|
|||
|
|
scope: global | tenant | sender_identity | campaign | coordination_case
|
|||
|
|
reason: string?
|
|||
|
|
source: provider | adapter | operator | participant
|
|||
|
|
created_at: timestamp
|
|||
|
|
expires_at: timestamp?
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Suppression should produce evidence:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
notification.channel.suppression_added
|
|||
|
|
notification.channel.suppression_removed
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
coordination-engine decides whether suppression means participant failure, channel failure, alternate channel selection, or manual review.
|
|||
|
|
|
|||
|
|
## 21. Open and Click Tracking Model
|
|||
|
|
|
|||
|
|
`email-connect` MAY support open and click tracking, but MUST classify such evidence conservatively.
|
|||
|
|
|
|||
|
|
### 21.1 Open Tracking
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
EmailOpenEvent:
|
|||
|
|
email_message_id: string
|
|||
|
|
occurred_at: timestamp
|
|||
|
|
ip_address: string?
|
|||
|
|
user_agent: string?
|
|||
|
|
proxy_classification: none | suspected | likely | confirmed
|
|||
|
|
confidence: low | medium | high
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Possible classifications:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
human_like_open
|
|||
|
|
proxy_open
|
|||
|
|
privacy_proxy_open
|
|||
|
|
image_proxy_open
|
|||
|
|
security_scanner_open
|
|||
|
|
unknown_open
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 21.2 Click Tracking
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
EmailClickEvent:
|
|||
|
|
email_message_id: string
|
|||
|
|
link_id: string
|
|||
|
|
occurred_at: timestamp
|
|||
|
|
url: string
|
|||
|
|
ip_address: string?
|
|||
|
|
user_agent: string?
|
|||
|
|
scanner_classification: none | suspected | likely | confirmed
|
|||
|
|
confidence: low | medium | high
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Possible classifications:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
human_like_click
|
|||
|
|
scanner_click
|
|||
|
|
link_prefetch
|
|||
|
|
multi_link_scan
|
|||
|
|
unverified_click
|
|||
|
|
unknown_click
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 21.3 Scanner Detection Hints
|
|||
|
|
|
|||
|
|
The adapter SHOULD consider:
|
|||
|
|
|
|||
|
|
* click occurs immediately after delivery
|
|||
|
|
* multiple links clicked within seconds
|
|||
|
|
* HEAD requests or unusual HTTP methods
|
|||
|
|
* known security user agents
|
|||
|
|
* known security vendor IP ranges
|
|||
|
|
* no browser-like behavior
|
|||
|
|
* link fetched but no subsequent portal session
|
|||
|
|
* repeated link checks from data center IPs
|
|||
|
|
* clicks before recipient likely had time to read
|
|||
|
|
|
|||
|
|
Scanner detection is heuristic and MUST be represented as confidence, not certainty, unless the adapter has strong provider-specific evidence.
|
|||
|
|
|
|||
|
|
## 22. Address and Endpoint Quality
|
|||
|
|
|
|||
|
|
`email-connect` SHOULD maintain email endpoint quality signals.
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
EmailEndpointQuality:
|
|||
|
|
endpoint_ref: EndpointRef
|
|||
|
|
syntax_valid: boolean?
|
|||
|
|
domain_exists: boolean?
|
|||
|
|
mx_exists: boolean?
|
|||
|
|
verified: boolean?
|
|||
|
|
catch_all_suspected: boolean?
|
|||
|
|
role_address_suspected: boolean?
|
|||
|
|
disposable_suspected: boolean?
|
|||
|
|
suppression_state: string?
|
|||
|
|
last_successful_endpoint_acceptance_at: timestamp?
|
|||
|
|
last_hard_bounce_at: timestamp?
|
|||
|
|
last_complaint_at: timestamp?
|
|||
|
|
last_engagement_at: timestamp?
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Endpoint quality may be emitted as diagnostics but should not by itself create coordination success.
|
|||
|
|
|
|||
|
|
## 23. Channel Health
|
|||
|
|
|
|||
|
|
`email-connect` SHOULD expose channel health.
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
EmailChannelHealth:
|
|||
|
|
sender_identity: string
|
|||
|
|
domain: string?
|
|||
|
|
provider_account_ref: string?
|
|||
|
|
status: healthy | degraded | failing | unknown
|
|||
|
|
authentication_status:
|
|||
|
|
spf: pass | fail | unknown
|
|||
|
|
dkim: pass | fail | unknown
|
|||
|
|
dmarc: pass | fail | unknown
|
|||
|
|
reputation_status: good | warning | poor | unknown
|
|||
|
|
bounce_rate: number?
|
|||
|
|
complaint_rate: number?
|
|||
|
|
deferral_rate: number?
|
|||
|
|
provider_degradation:
|
|||
|
|
- string
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Health-related normalized events:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
notification.channel.reputation_warning
|
|||
|
|
system.provider.degraded
|
|||
|
|
system.provider.unavailable
|
|||
|
|
system.adapter.health_changed
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 24. Security Requirements
|
|||
|
|
|
|||
|
|
`email-connect` MUST:
|
|||
|
|
|
|||
|
|
* protect provider credentials
|
|||
|
|
* verify provider webhook signatures where available
|
|||
|
|
* validate inbound webhook source authenticity where possible
|
|||
|
|
* avoid logging sensitive message content by default
|
|||
|
|
* preserve idempotency
|
|||
|
|
* prevent duplicate sends for repeated idempotency keys
|
|||
|
|
* support tenant or sender separation where applicable
|
|||
|
|
* avoid leaking tracking tokens
|
|||
|
|
* protect suppression lists
|
|||
|
|
* sanitize inbound replies and auto-replies
|
|||
|
|
|
|||
|
|
## 25. Privacy Requirements
|
|||
|
|
|
|||
|
|
`email-connect` SHOULD:
|
|||
|
|
|
|||
|
|
* store message content only when necessary
|
|||
|
|
* support metadata-only mode
|
|||
|
|
* support raw event redaction
|
|||
|
|
* support configurable retention of raw provider events
|
|||
|
|
* store endpoint references instead of endpoint values where possible
|
|||
|
|
* avoid storing unnecessary open/click details if not needed
|
|||
|
|
* allow tracking to be disabled
|
|||
|
|
* separate operational diagnostics from coordination evidence
|
|||
|
|
* support deletion or anonymization workflows where applicable
|
|||
|
|
|
|||
|
|
## 26. Reliability Requirements
|
|||
|
|
|
|||
|
|
`email-connect` MUST support:
|
|||
|
|
|
|||
|
|
* idempotent send requests
|
|||
|
|
* duplicate webhook event detection
|
|||
|
|
* out-of-order event handling
|
|||
|
|
* late bounce handling
|
|||
|
|
* retryable provider failures
|
|||
|
|
* non-retryable provider failures
|
|||
|
|
* provider timeout handling
|
|||
|
|
* correlation preservation
|
|||
|
|
* dead-letter handling for unprocessable events
|
|||
|
|
|
|||
|
|
Late events MUST be preserved.
|
|||
|
|
|
|||
|
|
Example:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
MX accepted at 10:00
|
|||
|
|
participant unresolved at 14:00
|
|||
|
|
hard bounce arrives at 18:00
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The hard bounce must still be recorded and emitted as evidence.
|
|||
|
|
|
|||
|
|
## 27. Raw Event Preservation
|
|||
|
|
|
|||
|
|
`email-connect` SHOULD preserve raw provider events or references to them.
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
RawEmailEventRef:
|
|||
|
|
raw_event_id: string
|
|||
|
|
provider_name: string
|
|||
|
|
storage_ref: string?
|
|||
|
|
received_at: timestamp
|
|||
|
|
redacted: boolean
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Normalized events should reference raw event data where available:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
raw_event_ref: raw_email_event_123
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 28. Minimal API Surface
|
|||
|
|
|
|||
|
|
`email-connect` SHOULD expose a headless API.
|
|||
|
|
|
|||
|
|
### 28.1 Adapter Contract API
|
|||
|
|
|
|||
|
|
Required conceptual operations:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
GET /adapter/descriptor
|
|||
|
|
GET /adapter/health
|
|||
|
|
POST /adapter/actions
|
|||
|
|
POST /adapter/events/provider
|
|||
|
|
GET /adapter/events
|
|||
|
|
GET /adapter/messages/{id}/timeline
|
|||
|
|
GET /adapter/messages/{id}/assessment
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The actual transport may differ, but these conceptual operations should exist.
|
|||
|
|
|
|||
|
|
### 28.2 Standalone API
|
|||
|
|
|
|||
|
|
Useful standalone operations:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
POST /email/send
|
|||
|
|
GET /email/messages/{id}
|
|||
|
|
GET /email/messages/{id}/timeline
|
|||
|
|
GET /email/messages/{id}/assessment
|
|||
|
|
GET /email/endpoints/{id}/quality
|
|||
|
|
POST /email/suppressions
|
|||
|
|
DELETE /email/suppressions/{id}
|
|||
|
|
GET /email/channel-health
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 29. Example End-to-End Flow
|
|||
|
|
|
|||
|
|
### 29.1 Secure Document Notification
|
|||
|
|
|
|||
|
|
1. `coordination-engine` creates a coordination case.
|
|||
|
|
2. `portal-connect` creates an authenticated document access link.
|
|||
|
|
3. `coordination-engine` sends `notification.send` to `email-connect`.
|
|||
|
|
4. `email-connect` renders and dispatches email.
|
|||
|
|
5. Provider accepts the message.
|
|||
|
|
6. `email-connect` emits `notification.attempt.accepted_by_provider`.
|
|||
|
|
7. Recipient MX accepts message.
|
|||
|
|
8. `email-connect` emits `notification.endpoint.accepted`.
|
|||
|
|
9. User clicks the link.
|
|||
|
|
10. `email-connect` emits `interaction.unverified_actor_interaction`.
|
|||
|
|
11. User logs into portal.
|
|||
|
|
12. `portal-connect` emits `identity.actor_authenticated`.
|
|||
|
|
13. User downloads document.
|
|||
|
|
14. `portal-connect` emits `delivery.payload.downloaded`.
|
|||
|
|
15. `coordination-engine` marks participant complete.
|
|||
|
|
|
|||
|
|
Email contributed useful path evidence but did not by itself prove the result.
|
|||
|
|
|
|||
|
|
### 29.2 Hard Bounce and Fallback
|
|||
|
|
|
|||
|
|
1. `email-connect` sends email.
|
|||
|
|
2. Provider accepts the message.
|
|||
|
|
3. Recipient server returns hard bounce.
|
|||
|
|
4. `email-connect` emits `notification.endpoint.rejected_permanent`.
|
|||
|
|
5. `coordination-engine` marks email channel failed for that participant.
|
|||
|
|
6. Policy engine selects SMS fallback or manual review.
|
|||
|
|
|
|||
|
|
### 29.3 Scanner Click
|
|||
|
|
|
|||
|
|
1. Email is accepted by MX.
|
|||
|
|
2. Link is clicked immediately by a known scanner pattern.
|
|||
|
|
3. `email-connect` emits `interaction.scanner_or_bot_interaction`.
|
|||
|
|
4. `coordination-engine` keeps participant in `undef.identity_uncertain`.
|
|||
|
|
5. No reminder is suppressed merely because of the scanner click.
|
|||
|
|
6. If no portal evidence appears after threshold, reminder or alternate channel is triggered.
|
|||
|
|
|
|||
|
|
## 30. Provider Implementation Guidance
|
|||
|
|
|
|||
|
|
The first real provider integration SHOULD be selected based on:
|
|||
|
|
|
|||
|
|
* webhook support
|
|||
|
|
* bounce event quality
|
|||
|
|
* provider metadata/correlation support
|
|||
|
|
* suppression API
|
|||
|
|
* event reliability
|
|||
|
|
* local availability and cost
|
|||
|
|
* compatibility with transactional email
|
|||
|
|
* ability to separate message streams
|
|||
|
|
|
|||
|
|
The implementation SHOULD avoid hardcoding provider semantics into the core email model.
|
|||
|
|
|
|||
|
|
Provider-specific modules should map to the email-native model first, then to normalized coordination events.
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Provider event
|
|||
|
|
→ email-native event
|
|||
|
|
→ EmailEvidenceAssessment
|
|||
|
|
→ EvidenceEvent for coordination-engine
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 31. Message Steparation
|
|||
|
|
|
|||
|
|
`email-connect` SHOULD support sender identities or message streams.
|
|||
|
|
|
|||
|
|
Recommended streams:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
transactional
|
|||
|
|
notification
|
|||
|
|
legal_or_high_assurance_notice
|
|||
|
|
marketing
|
|||
|
|
system_alert
|
|||
|
|
test
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
High-assurance or legally relevant notifications SHOULD NOT share reputation-critical infrastructure with marketing traffic unless explicitly accepted by policy.
|
|||
|
|
|
|||
|
|
Stream separation may affect:
|
|||
|
|
|
|||
|
|
* sender domain
|
|||
|
|
* return path
|
|||
|
|
* DKIM identity
|
|||
|
|
* provider account
|
|||
|
|
* suppression scope
|
|||
|
|
* complaint handling
|
|||
|
|
* deliverability diagnostics
|
|||
|
|
|
|||
|
|
## 32. Legal and Compliance Disclaimer
|
|||
|
|
|
|||
|
|
`email-connect` does not by itself provide legal proof of delivery, legal notice, acceptance, signature, or contract closure.
|
|||
|
|
|
|||
|
|
It provides evidence from the email channel.
|
|||
|
|
|
|||
|
|
Scenario-specific applications and `coordination-engine` policies may combine email evidence with stronger evidence from portal, identity, signature, payment, archive, or manual processes.
|
|||
|
|
|
|||
|
|
The adapter MUST avoid naming technical email events in ways that imply legal success.
|
|||
|
|
|
|||
|
|
For example:
|
|||
|
|
|
|||
|
|
Use:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
notification.endpoint.accepted
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Avoid:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
recipient_notified
|
|||
|
|
legal_delivery_completed
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 33. MVP Scope
|
|||
|
|
|
|||
|
|
The first useful version of `email-connect` should implement:
|
|||
|
|
|
|||
|
|
1. Adapter descriptor.
|
|||
|
|
2. Adapter health endpoint.
|
|||
|
|
3. `notification.send`.
|
|||
|
|
4. Idempotent send request handling.
|
|||
|
|
5. Simulated provider or one real provider.
|
|||
|
|
6. Email message and attempt records.
|
|||
|
|
7. Provider event ingestion.
|
|||
|
|
8. Basic bounce classification.
|
|||
|
|
9. Basic open/click classification.
|
|||
|
|
10. Evidence event generation.
|
|||
|
|
11. Message timeline.
|
|||
|
|
12. Message assessment.
|
|||
|
|
13. Suppression support.
|
|||
|
|
14. Mapping to AdapterInterfaceSpecification.md v1.0.
|
|||
|
|
|
|||
|
|
### MVP Required Email Events
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
notification.attempt.accepted_by_adapter
|
|||
|
|
notification.attempt.rejected_by_adapter
|
|||
|
|
notification.attempt.accepted_by_provider
|
|||
|
|
notification.attempt.rejected_by_provider
|
|||
|
|
notification.endpoint.accepted
|
|||
|
|
notification.endpoint.deferred
|
|||
|
|
notification.endpoint.rejected_temporary
|
|||
|
|
notification.endpoint.rejected_permanent
|
|||
|
|
interaction.proxy_or_privacy_interaction
|
|||
|
|
interaction.scanner_or_bot_interaction
|
|||
|
|
interaction.unverified_actor_interaction
|
|||
|
|
notification.channel.complaint_received
|
|||
|
|
notification.channel.suppression_added
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### MVP Acceptance Criteria
|
|||
|
|
|
|||
|
|
The MVP is acceptable when it can:
|
|||
|
|
|
|||
|
|
1. Accept a coordination-compatible send request.
|
|||
|
|
2. Dispatch or simulate an email.
|
|||
|
|
3. Preserve correlation and idempotency.
|
|||
|
|
4. Ingest or simulate provider events.
|
|||
|
|
5. Produce normalized evidence events.
|
|||
|
|
6. Classify MX acceptance as weak technical evidence.
|
|||
|
|
7. Classify hard bounce as strong failure evidence.
|
|||
|
|
8. Classify proxy opens and scanner clicks as ambiguous.
|
|||
|
|
9. Provide a message timeline.
|
|||
|
|
10. Provide an email evidence assessment.
|
|||
|
|
11. Integrate with coordination-engine without overclaiming success.
|
|||
|
|
|
|||
|
|
## 34. Future Extensions
|
|||
|
|
|
|||
|
|
Potential future capabilities:
|
|||
|
|
|
|||
|
|
* multi-provider routing
|
|||
|
|
* provider failover
|
|||
|
|
* domain reputation monitoring
|
|||
|
|
* inbox placement seed testing
|
|||
|
|
* DMARC aggregate report ingestion
|
|||
|
|
* BIMI diagnostics
|
|||
|
|
* advanced bounce parsing
|
|||
|
|
* advanced scanner detection
|
|||
|
|
* inbound reply classification
|
|||
|
|
* natural-language reply intent extraction
|
|||
|
|
* tenant-specific suppression scopes
|
|||
|
|
* message stream isolation
|
|||
|
|
* deliverability analytics
|
|||
|
|
* adaptive sending policies
|
|||
|
|
* integration with archive systems
|
|||
|
|
* S/MIME or PGP support
|
|||
|
|
* signed email support
|
|||
|
|
* email authentication diagnostics
|
|||
|
|
* AI-assisted deliverability analysis
|
|||
|
|
|
|||
|
|
## 35. Non-Goals
|
|||
|
|
|
|||
|
|
`email-connect` is not:
|
|||
|
|
|
|||
|
|
* a marketing automation platform
|
|||
|
|
* a newsletter campaign manager
|
|||
|
|
* a CRM
|
|||
|
|
* a full workflow engine
|
|||
|
|
* a legal delivery service by itself
|
|||
|
|
* a document portal
|
|||
|
|
* a payment system
|
|||
|
|
* a signature system
|
|||
|
|
* the owner of coordination case success
|
|||
|
|
|
|||
|
|
It may integrate with such systems or be used by them.
|
|||
|
|
|
|||
|
|
## 36. Summary
|
|||
|
|
|
|||
|
|
`email-connect` models email as a useful but uncertain communication and notification channel.
|
|||
|
|
|
|||
|
|
Its job is to:
|
|||
|
|
|
|||
|
|
* send emails
|
|||
|
|
* ingest provider events
|
|||
|
|
* classify email outcomes
|
|||
|
|
* normalize email evidence
|
|||
|
|
* preserve message timelines
|
|||
|
|
* expose endpoint and channel diagnostics
|
|||
|
|
* integrate cleanly with `coordination-engine`
|
|||
|
|
|
|||
|
|
The key rule is:
|
|||
|
|
|
|||
|
|
> Email events are evidence, not result satisfaction. email-connect reports email-channel facts and uncertainty. coordination-engine evaluates intended results.
|
|||
|
|
|