Implementation Plan: Email Providers
Feature ID: email-providers
Jira Epic: EW-650
Branch: feat/notifications-v2-multichannel (umbrella branch for the 4-spec rollout)
Spec: spec.md (v1.1)
Status: In Progress
Last updated: 2026-05-28
Phase 1 — Plugin contracts & capabilities
- P1.1 Declare
PLUGIN_CAPABILITIES.EMAIL_OUTBOUNDandPLUGIN_CAPABILITIES.EMAIL_INBOUNDinpackages/plugin/src/contracts/capabilities/. - P1.2 Define
IEmailOutboundPluginandIEmailInboundPlugininterfaces (see spec §3.2/§3.3). - P1.3 Define canonical DTOs:
EmailSendInput,EmailSendResult,EmailInboundMessage,EmailDeliveryEvent,EmailVerification,EmailAttachment. - P1.4 Add capability discovery helpers to
plugin-registry.service.ts(getOutboundEmailProviders(),getInboundEmailProviders()).
Phase 2 — Data model
- P2.1 Entities under
packages/agent/src/entities/:tenant-email-address.entity.tsagent-email-assignment.entity.tsemail-message.entity.tsemail-conversation.entity.ts(v1.1)
- P2.2 Migration
apps/api/src/migrations/<unix-millis>-AddEmailProvidersTables.ts. Includes the partial unique indexes called out in spec §4.1. - P2.3 Repositories in
packages/agent/src/database/repositories/.
Phase 3 — EmailFacadeService
- P3.1
packages/agent/src/facades/email.facade.tsmirroringAiFacadeServiceshape. - P3.2 Resolution priority: explicit address override → Agent default → first-enabled provider for the capability.
- P3.3 Settings resolution via the 4-level hierarchy (Work → User → Admin → Plugin defaults).
- P3.4 Per-call
EmailFacadeOptionswithuserId,workId?,agentId?,taskId?for attribution. - P3.5
PluginUsageEventemission on every send (capability='email'). - P3.6 React-Email rendering path: when
template.kind === 'react', render via@react-email/renderbefore handoff to plugin.
Phase 4 — REST API surface
- P4.1
apps/api/src/email/email.controller.ts— tenant address CRUD, list, verify. - P4.2 Webhook routes:
POST /api/email/inbound/:pluginId,POST /api/email/events/:pluginId,GET /api/email/verify/:tokenId. - P4.3 Webhook signature verification per plugin; 401 with no body on mismatch (don't leak which secret is wrong).
- P4.4 Rate-limit webhook endpoints (default 600/min per plugin id) via
@nestjs/throttler.
Phase 5 — Agent integration
- P5.1
sendEmailtool descriptor inpackages/agent/src/agents/tools/gated oncanCallExternalTools+ ≥1 outbound assignment. - P5.2
AGENT_INBOUND_EMAIL_DISPATCHERinjection token with default binding that resolves Agent → Task or Agent → EmailConversation per assignment mode. - P5.3
messageAgenttool descriptor (spec §12.4) — resolves target Agent's primary inbound address. - P5.4 AGENT_GIT_FACADE updates: read
Agent.committerEmail, fall back to<slug>@agents.ever.worksplaceholder. - P5.5 New
EMAIL_SENT/EMAIL_RECEIVEDactivity-log actions.
Phase 6 — Provider plugins (one PR per provider — see tasks.md)
- P6.1
packages/plugins/postmark/— reference implementation, outbound + inbound. - P6.2
packages/plugins/resend/— outbound only (Resend inbound still private beta). - P6.3
packages/plugins/mailgun/— outbound + inbound. - P6.4
packages/plugins/sendgrid/— outbound + inbound. - P6.5
packages/plugins/mailchimp-transactional/— outbound + inbound. - P6.6
packages/plugins/local-smtp/— dev/self-host fallback vianodemailer, outbound only.
Phase 7 — Web UI (handed off to agent-inbox-ui)
The settings UI (/settings/integrations/emails) and the per-Agent inbox surfaces live in the sibling agent-inbox-ui spec. This epic only owns the API surface they consume.
Phase 8 — React-Email templates
- P8.1
pnpm add @react-email/components @react-email/rendertoapps/api. - P8.2 Template directory
apps/api/src/email/templates/react/with at least one reference template (agent-summary.tsx). - P8.3 Zod schemas co-located with each template export the typed
Propsshape so the composer UI can build a form for them.
Phase 9 — Testing
- P9.1 Unit tests for
EmailFacadeServicecovering resolution + attribution. - P9.2 Unit tests per provider plugin (Vitest) covering happy path + signature failure.
- P9.3 E2E test: register Postmark outbound address → trigger verification → assign to Agent → trigger
sendEmailfrom agent run → assertPluginUsageEventrow. - P9.4 E2E test: inbound webhook (mocked Postmark POST) → message lands in
email_messages→ Task spawned.
Phase 10 — Docs
- P10.1 Update
apps/docs/with operator-facing "Connect an email provider" guide. - P10.2 Add per-provider setup notes (Postmark/Mailgun/Sendgrid/Resend webhook URLs operators must register).
- P10.3 Update
docs/specs/architecture/database.mdwith the new tables.
Dependencies & cross-spec coordination
- Independent of:
notification-channels— both share the plugin-capabilities foundation but ship on separate timelines. - Consumed by:
event-subscriptions— once email is a registered delivery channel, the subscriptions surface treats it like any other channel. - UI implemented by:
agent-inbox-ui.
Out of scope (mirrors spec §8)
Reply-by-email SMTP threading (v2), template management UI (v2), S/MIME / PGP (no plan), multi-tenant routing (waits for multi-tenancy track).