Last Updated: 2026-05-06 Status: Draft (Vision) Service:
apps/communication,apps/newsletter-service,apps/seller-dashboardDepends on: aws-ses-migration.md, contact-intelligence, ai-brain
This doc explores five capabilities Vlozi can build by combining Cloudflare's edge primitives with AWS SES's deliverability moat. None of them are unique inventions — Mailchimp, Klaviyo, and HubSpot have analogues — but each costs them millions in dedicated infra. On the CF + SES combo, several become near-free.
This is a vision and sequencing doc, not a committed implementation plan. Each capability gets its own implementation doc when promoted to a sprint.
1. Purpose
1.1 The Strategic Bet
Vlozi's wedge is the multi-tenant blog + newsletter + contact-intelligence loop. The communication layer is not a commodity in this product — it's the surface where most differentiating features live (replies → memory, AI-personalized sends, send-time optimization).
Building these on a generic ESP API (Resend / Postmark) means accepting their feature ceiling. Building on AWS SES + Cloudflare's edge primitives gives us:
- AWS SES for the boring-but-hard-won parts: IP reputation, postmaster relationships, deliverability, scale.
- Cloudflare for the per-recipient, edge-state, and AI-augmented parts: Durable Objects, Vectorize, Workers AI, Email Routing, DNS API.
Combining them lets Vlozi ship features that pure-Resend and pure-AWS competitors can't easily match.
1.2 Target Audience
- Vlozi engineering — capability roadmap and sequencing
- Vlozi product/founder — feature positioning vs. incumbents
- Future hires — "why does our email stack look like this?"
1.3 Out of Scope
- Implementation specs for each capability — those become their own
*-spec.mddocs when scheduled. - Provider migration mechanics — covered in aws-ses-migration.md.
- Tenant pricing of the resulting features — billing-side decision.
2. Capability Matrix
What each platform contributes to the email stack:
| Concern | AWS SES | Cloudflare | Notes |
|---|---|---|---|
| Outbound transport | ✅ Native | ❌ | SES has the IPs and the reputation |
| Inbound reply receiving | ⚠️ Receipt rules + S3 | ✅ Email Routing + Email Workers | CF is simpler for this |
| Per-tenant domain verification | ✅ Unlimited identities | ❌ | SES has the API |
| DKIM auto-publishing | ❌ | ✅ DNS API | CF DNS is the edit surface for ~20% of domains |
| Per-recipient state (engagement, prefs) | ⚠️ DynamoDB | ✅ Durable Objects | DOs are cheaper and stickier per entity |
| Per-recipient timer mesh (send-time) | ⚠️ EventBridge (heavy) | ✅ DO alarms | Native primitive on CF; bolt-on on AWS |
| Embeddings + vector search | ⚠️ Bedrock + Pinecone (paid) | ✅ Vectorize (cheap) | CF is 10× cheaper for newsletter-scale |
| Small-LLM inference at request-time | ⚠️ Bedrock (paid, slow cold start) | ✅ Workers AI (fast, cheap) | CF wins for per-send personalization |
| Bulk LLM (large model, batch) | ✅ Bedrock | ⚠️ via API | AWS for AI Brain heavy lifts |
| Event archival | ⚠️ S3 + Glue + Athena | ✅ R2 + Analytics Engine | Both cheap; CF zero-egress |
| Multi-region compute | ⚠️ Lambda@Edge (limited) | ✅ Workers (300+ POPs) | CF is the edge platform |
| IAM/secrets | ✅ KMS + IAM | ⚠️ Secrets only | Use AWS for cross-account isolation later |
3. The Five Capabilities
4. Capability Specs
4.1 Bidirectional Replies → Contact Intelligence
4.1.1 The Pitch
"Your subscribers can reply to newsletters. Vlozi captures every reply, threads it to the contact, and your AI Brain remembers the conversation forever."
Every newsletter SaaS treats replies as a dropped feature. We turn them into the highest-signal contact data the product has.
4.1.2 Architecture
4.1.3 Reply Addressing Scheme
| Approach | Pros | Cons |
|---|---|---|
reply+<sendId>@vlozi.app (platform domain) |
Centralized; Email Routing on one domain | Reply visibly comes from Vlozi, not tenant |
reply+<sendId>@news.<tenant-domain> |
Replies stay on tenant's brand | Requires per-tenant MX setup; conflicts with tenant's existing inbound mail |
reply+<sendId>@news.brand.com with explicit subdomain delegation |
Brand-aligned, no MX conflict | One more DNS record for the tenant |
Decision: option 3. During domain verification (Phase 2 of SES migration), we ask the tenant to also publish an MX record for the news. subdomain pointing to Cloudflare Email Routing. The Domain-as-Code flow (§4.4) auto-publishes this for CF DNS users.
4.1.4 New Schema
// In newsletter-service or contact-intelligence schema
export const inboundReplies = pgTable("nl_inbound_replies", {
id: text("id").primaryKey(), // rep_<nanoid>
tenantId: text("tenant_id").notNull(),
sendId: text("send_id").notNull(), // → campaignSends.id
subscriberId: text("subscriber_id").notNull(),
campaignId: text("campaign_id").notNull(),
fromAddress: text("from_address").notNull(),
subject: text("subject"),
bodyText: text("body_text"), // plain-text, stripped of quoted reply
bodyHtml: text("body_html"),
rawMimeR2Key: text("raw_mime_r2_key"), // full original in R2 for audit
// Verification
dkimResult: text("dkim_result"), // pass | fail | none
dmarcResult: text("dmarc_result"),
spamScore: integer("spam_score"),
// Brain integration
threadedAt: timestamp("threaded_at"), // when CI ingested it
brainProcessedAt: timestamp("brain_processed_at"),
receivedAt: timestamp("received_at").defaultNow().notNull(),
})4.1.5 Email Worker Logic
export default {
async email(message: ForwardableEmailMessage, env: Env) {
const sendId = parseSendIdFromTo(message.to) // "reply+abc123@news.brand.com" → "abc123"
if (!sendId) {
message.setReject("Unknown recipient")
return
}
// DKIM/DMARC are pre-verified by Cloudflare; we read the headers
const dkim = message.headers.get("Authentication-Results-DKIM")
const dmarc = message.headers.get("Authentication-Results-DMARC")
// Stream raw MIME to R2 for audit
const rawKey = `inbound-replies/${tenantId}/${sendId}.eml`
await env.R2.put(rawKey, message.raw)
// Strip quoted history, extract reply body
const parsed = await parseMime(message.raw)
const cleanText = stripQuotedReply(parsed.text)
// POST to newsletter-service /internal/inbound-reply
await env.NEWSLETTER_SERVICE.fetch(/* ... */)
}
}4.1.6 Cost
- CF Email Routing: free (10k inbound/day per zone limit, more on paid plans)
- CF Email Workers: free at modest volume; counts against Workers paid plan
- R2 storage: ~$0.015/GB-month for raw MIME archive
- Per reply estimated cost: ~$0.0001
Effectively free at any reasonable newsletter scale.
4.1.7 Build Effort
~3 days on top of the SES migration:
- 0.5d — MX setup in Domain-as-Code flow
- 1d — Email Worker (parse, DKIM check, R2 write, dispatch)
- 1d — Newsletter
/internal/inbound-replyroute + schema migration - 0.5d — Contact Intelligence ingestion stub (real ingestion lives in CI service)
4.1.8 Dependencies
- ✅ SES migration (provides verified domains)
- ✅ Domain-as-Code (§4.4) for the MX auto-setup — strongly preferred
- ⏸ Contact Intelligence service must exist to consume replies. Until CI ships, replies write to a holding table; backfill on CI launch.
4.1.9 Risks
| Risk | Mitigation |
|---|---|
Spam to reply+*@news.brand.com |
DKIM/DMARC enforcement at Email Worker; spam score threshold |
| Auto-reply loops (vacation responders) | Detect Auto-Submitted header, drop |
| Tenant abuse — using replies as a free inbox | Rate-limit per tenant; flag if >X% of "subscribers" are sending non-newsletter mail |
| MX conflict with tenant's existing mail | Subdomain (news.) keeps it separate; explicit warning at setup |
4.2 AI-Personalized Email at Edge
4.2.1 The Pitch
"AI rewrites the intro of every email for every subscriber, based on what they've clicked before. Open rates jump 30–50% on premium sends."
Per-recipient personalization is currently a Mailchimp Premium feature ($350+/mo). We build it on Workers AI + Vectorize for fractional cost, exposed as an opt-in "Premium Send" toggle per campaign.
4.2.2 Architecture
4.2.3 Data Model
| Table | Purpose |
|---|---|
subscriber_engagement_events |
Append-only log of opens/clicks (already have via messageEvents) |
subscriber_embeddings (in Vectorize) |
768-dim vector per subscriber, refreshed nightly from event history |
campaign_personalization_config |
Per-campaign — flag whether to personalize, prompt template, fallback intro |
The Vectorize entry's metadata stores derived signals (top_categories: ["pricing", "case-studies"], engagement_score: 0.74, last_active: 2026-04-30) — used as features in the prompt, not just as raw vectors.
4.2.4 Personalization Prompt
const intro = await env.AI.run("@cf/meta/llama-3-8b-instruct", {
messages: [
{ role: "system", content: campaign.personalizationConfig.systemPrompt },
{
role: "user",
content: `Subscriber engagement summary:
- Top interests: ${meta.top_categories.join(", ")}
- Last active: ${meta.last_active}
- Past clicks: ${meta.click_count}
Newsletter topic: ${campaign.topic}
Write a 1–2 sentence personalized opening line.`
}
],
max_tokens: 100,
})The output replaces a {{ ai_intro }} token in the campaign HTML.
4.2.5 Cost
| Item | Cost |
|---|---|
| Workers AI llama-3-8b call | ~$0.0001 per call |
| Vectorize lookup | ~$0.00001 per query |
| Per-recipient personalization | ~$0.00011 |
For a 10k personalized blast: ~$1.10. For 100k: ~$11. Pass-through pricing as a "Premium Send" — charge tenants $5–10 per personalized blast.
4.2.6 Build Effort
~7 days:
- 2d — Embedding pipeline (engagement events → nightly batch → Vectorize upsert)
- 2d — Per-send personalization in queue consumer
- 1d — Campaign UI: "Premium Send" toggle, prompt editor
- 1d — Fallback handling (Workers AI errors must not block sends)
- 1d — A/B testing harness so tenants can measure lift
4.2.7 Dependencies
- ⏸ AI Brain Phase 1 must ship first (sets up the prompt-engineering patterns; see ai-brain implementation)
- ✅ SES migration
4.2.8 Tradeoffs
- Latency penalty — adds ~200–500ms per send (model inference). At 14/sec SES default rate, this doesn't bottleneck.
- Personalization can backfire — a subtly wrong intro feels worse than a generic one. Heavy QA + tenant preview required before enabling.
- Privacy posture — embedding subscriber behavior into a vector DB needs to be in the privacy policy. Use existing patterns from contact-intelligence security guide.
4.3 Send-Time Optimization via Per-Subscriber DO Mesh
4.3.1 The Pitch
"Vlozi delivers each email at the moment your subscriber is most likely to open it. No tuning needed — the system learns continuously."
This is the headline feature in Mailchimp's $350/mo tier. We build it as a native primitive on Cloudflare Durable Object alarms.
4.3.2 Architecture
4.3.3 The Histogram
Each SubscriberSendDO instance keeps a 168-bucket histogram (24 hours × 7 days) in DO storage:
class SubscriberSendDO {
private histogram: number[] = new Array(168).fill(0) // hour-of-week → opens
private timezone: string | null = null // inferred from User-Agent + IP
// Called whenever an open/click event arrives
async recordEngagement(eventTime: Date) {
const localHour = this.toLocalHourOfWeek(eventTime)
this.histogram[localHour] += 1
// Decay older entries (multiplicative, weekly)
if (this.recordsThisWeek++ > DECAY_THRESHOLD) {
this.histogram = this.histogram.map(v => v * 0.95)
this.recordsThisWeek = 0
}
await this.storage.put("histogram", this.histogram)
}
predictOptimalSendTime(): Date {
if (totalEngagements < 5) return Date.now() // not enough data, send now
const bestBucket = argmax(this.histogram)
return nextOccurrence(bestBucket, this.timezone)
}
}4.3.4 Send Flow
| Step | Where | Action |
|---|---|---|
| 1 | fireCampaign |
Enqueues N jobs as today |
| 2 | Queue consumer | For each job, routeToDO(subscriberId) |
| 3 | DO | Reads histogram, computes optimal send time |
| 4 | DO | If optimal is within 24h, setAlarm. If "now," dispatches immediately. |
| 5 | DO alarm() |
Calls comms /internal/send → SES |
Cap: never delay >24h. If a tenant clicks "Send now," they expect the email to arrive today.
4.3.5 Cost
- DO storage: ~1KB per subscriber × 10k subs = 10MB ≈ $0.20/mo
- DO requests: 1 read + 1 write per send + 1 alarm = 3 requests per send. At 100k sends, ~300k requests = $0.04
- Total per 100k campaign: ~$0.05 marginal cost
4.3.6 Build Effort
~5 days:
- 1d —
SubscriberSendDOclass + histogram math - 1d — Queue consumer routing + alarm scheduling
- 1d — Engagement event ingestion (open/click → DO
recordEngagement) - 1d — Timezone inference (IP geolocation + UA hints; default to tenant's TZ)
- 1d — Tenant UI toggle + "best time to send" insight in dashboard
4.3.7 Dependencies
- ✅ SES migration (per-send dispatch must work)
- ✅ Engagement events must flow back through SES → SNS webhook (already in v1)
- Bidirectional replies (§4.1) — replies are an engagement signal too
4.3.8 The SES Rate-Limit Caveat
SES default sending rate is 14 emails/sec (raisable). If 10k subscribers are all "optimal at 8am," the 8am wave saturates the rate limit and pushes the tail into 9am.
Mitigations:
- Spread the 8am bucket across the surrounding 30 min (jitter)
- Request SES rate increase early (filed at SES migration Phase 1)
- For very large campaigns, pair with edge fan-out (§4.5)
4.4 Domain-as-Code (CF DNS API + SES)
4.4.1 The Pitch
"Sign in with Cloudflare. Your sending domain is verified, DKIM-signed, and DMARC-protected in 60 seconds. No DNS panel, no copy-paste."
This is the difference between "newsletter setup is an hour of work" and "newsletter setup is an instant." For tenants whose domain is on Cloudflare DNS (~20% globally, ~50% of tech-savvy SaaS customers), it's a strict UX upgrade.
4.4.2 Architecture
4.4.3 What Gets Auto-Published
For tenants on Cloudflare DNS, the auto-setup writes:
| Record | Purpose |
|---|---|
3× CNAME *._domainkey.news.brand.com |
DKIM signing keys |
TXT _dmarc.news.brand.com = v=DMARC1; p=quarantine; rua=mailto:dmarc@vlozi.app |
DMARC policy with aggregate reporting to us |
MX news.brand.com → route1.mx.cloudflare.net (+ priority backup) |
Inbound replies via Email Routing |
TXT news.brand.com = v=spf1 include:amazonses.com ~all |
SPF (defensive — DKIM is the primary auth) |
4.4.4 OAuth Scope
Cloudflare API token scope: Zone.DNS:Edit on the single zone the tenant selects. Token stored encrypted (KMS or Workers Secrets) per tenant. Token can be revoked by the tenant from their Cloudflare dashboard at any time — we surface revocation events as "DNS sync paused."
4.4.5 Drift Detection
A weekly cron (or DO alarm — pattern from feedback_cron_to_do_scheduler.md) re-checks each verified domain:
- Read current DNS records via CF API
- Compare against expected
- If drift detected:
- Auto-repair if minor (record removed)
- Alert tenant if major (DMARC weakened, MX changed)
4.4.6 Build Effort
~4 days:
- 1d — CF OAuth integration (token request + storage)
- 1d — DNS record templating + write API
- 1d — Provider detection (
dig NS) + fallback to manual instructions - 1d — Drift detection cron + alerting
4.4.7 Dependencies
- ✅ SES migration (provides the DKIM tokens to publish)
- Bidirectional replies (§4.1) — MX setup is for that feature, included in the auto-setup
4.4.8 The Non-CF Path
Tenants on Route 53, GoDaddy, Namecheap, etc., get the manual flow — copy-paste 3 CNAMEs, click "Check now." This is what the SES migration doc Phase 3 already covers. Domain-as-Code is additive UX for the CF subset.
4.4.9 Future Extensions
| Provider | Approach |
|---|---|
| Route 53 | AWS API + IAM cross-account role (more complex; defer) |
| Namecheap, GoDaddy, etc. | Their APIs are weaker; not worth it |
| Manual | Always supported as fallback |
Vlozi only needs to integrate Cloudflare DNS to cover the dominant DIY-SaaS demographic. Other providers stay manual.
4.5 Multi-Region Edge Fan-Out
4.5.1 The Pitch
"100k recipients, blasted globally in 8 minutes instead of 80. Each email sent from the AWS region closest to the recipient — better latency, better deliverability."
This is the "we hit serious newsletter scale" feature. It exists today only at companies with dedicated email infra teams (Klaviyo, Iterable). On CF + SES it's tractable for a small team.
4.5.2 Architecture
4.5.3 Region Inference
Per recipient, infer region from:
- Recipient's email domain → MX → IP → geolocation (cached)
- Stored
subscriber.regionif previously inferred - Default to tenant's home region
4.5.4 Per-Region Identity Setup
This is the operational tax. Each region needs:
- A separate SES Email Identity (DKIM keys differ per region)
- Per-region Configuration Sets
- Per-region SNS topics → consolidated to one CF webhook handler
- Per-region rate limit increase ticket
For tenants with verified domains, the domain must be re-verified in each region. Domain-as-Code (§4.4) handles this transparently — auto-publish 3 DKIM CNAMEs per region (12 records total for 4 regions).
4.5.5 Cost
- Marginal SES cost: same ($0.10/k regardless of region)
- Worker invocations: 4× more (router + per-region) but Workers are basically free
- Operational complexity: significantly higher — debugging across regions, tracking per-region quotas
4.5.6 Build Effort
~10 days:
- 2d — Region router + recipient region cache
- 2d — Per-region SES setup (Terraform or scripted)
- 2d — Per-region webhook ingestion to one consolidated event stream
- 2d — Multi-region domain verification flow
- 2d — Tenant dashboard region analytics ("23k delivered from EU, 18k from US...")
4.5.7 When to Build
Trigger conditions (any one):
- Single tenant >100k emails per campaign
- Total platform >1M emails/month
- Customer complains about delivery latency in their region
- Two or more tenants ask for "send from EU only" for GDPR posture
Until then, single-region SES (ap-south-1) is fine. India + Asia recipients fast, US/EU recipients see ~150ms send latency which is invisible at email scale.
5. Sequencing & Dependency Graph
5.1 Recommended Build Order
| Order | Capability | Trigger | Why now |
|---|---|---|---|
| 1 | SES Migration | Committed | Foundation. See aws-ses-migration.md. |
| 2 | Domain-as-Code (§4.4) | Bundle with migration Phase 3 | Onboarding UX. Cheap to build during migration; expensive to retrofit. |
| 3 | Bidirectional Replies (§4.1) | Within 2 weeks of launch | Differentiates Vlozi from every other newsletter SaaS. Feeds Contact Intelligence. |
| 4 | Send-Time Optimization (§4.3) | First "premium tier" feature | Pricing differentiator. Free to build, sells at premium. |
| 5 | AI Personalization (§4.2) | After AI Brain Phase 1 ships | Premium send tier. Per-blast cost; pass-through pricing. |
| 6 | Edge Fan-Out (§4.5) | At 1M emails/mo OR multi-region tenant ask | Operational complexity only justifies at scale. |
6. Cumulative Cost Model
Assuming a tenant sends 30k emails/month with 30% on premium personalized sends, mixed engagement rate ~25%, distributed globally:
| Component | Cost/mo | Notes |
|---|---|---|
| AWS SES outbound | $3.00 | 30k × $0.10/k |
| AWS SES events → SNS | $0.02 | ~75k notifications |
| CF Workers (queue, consumers, DOs) | $0.30 | within paid tier |
| CF Email Routing (replies) | $0.00 | free tier |
| CF R2 (raw MIME archive) | $0.05 | ~3GB stored |
| CF Vectorize (subscriber embeddings) | $0.50 | ~10k embeddings |
| CF Workers AI (premium sends) | $1.00 | 9k personalized × $0.0001 |
| CF DO storage (send-time histograms) | $0.20 | ~10k subscriber DOs |
| Total | ~$5.07 |
For a 1,000-tenant SaaS at this volume mix: ~$5,000/mo total infra cost on a $30M ARR-potential business. The unit economics are spectacular.
NOTE
Add ~$100/mo for AWS Business Support once production. Not optional at scale.
7. Risks & Open Questions
| # | Risk / Question | Mitigation / Owner |
|---|---|---|
| 1 | Cloudflare Email Routing reliability — what's the SLA? | No formal SLA. Acceptable for inbound replies (lossy is OK); not for outbound. |
| 2 | Workers AI model availability — small models can deprecate | Pin to specific model versions; have a fallback prompt path |
| 3 | Vectorize is still maturing as a product | Acceptable — embeddings are derived data, can rebuild from event log |
| 4 | DO storage limits (10GB per DO, 50GB per account) — could hit at multi-million-subscriber scale | Shard subscribers across multiple DO classes; revisit at 1M+ subscribers |
| 5 | Multi-region SES = multi-region DKIM = multi-region domain verification UX | Folded into Domain-as-Code; explicit warning on UI for non-CF DNS tenants |
| 6 | Tenant fork — what if a customer wants AWS-only or CF-only for compliance reasons? | Defer until asked; current architecture has clean bindings to swap either side |
| 7 | OAuth token storage for CF DNS access — KMS or Workers Secrets? | KMS encrypts at rest, Workers Secrets is convenient. Use KMS, decrypt in Worker. |
| 8 | Reply abuse — using Vlozi as a free inbox via newsletter "subscribers" | Per-tenant rate limits + reply-volume anomaly detection |
| 9 | DMARC p=reject vs p=quarantine for tenant domains |
Start p=quarantine, recommend p=reject after 30d clean |
| 10 | Region inference accuracy at recipient level — domain MX often misleads | Acceptable — even imperfect routing is better than single-region for global lists |
8. Positioning & Marketing Implications
The capabilities here suggest specific launch positioning that differentiates Vlozi from incumbents:
| Capability | Marketing copy direction |
|---|---|
| Domain-as-Code | "Set up your sending domain in 60 seconds, not 60 minutes." |
| Bidirectional Replies + CI | "Your subscribers reply. Your AI remembers. Your team responds in their voice." |
| Send-Time Optimization | "Every email arrives at the moment your subscriber is most likely to open it." |
| AI Personalization | "Premium sends rewrite the intro for every subscriber, based on what they care about." |
Per Vlozi anonymous brand posture, all copy should be brand-voice; no founder narrative. These are capability-led positioning angles, not personal-story angles.
9. References
Internal
- AWS SES Migration Plan — committed migration
- Communication Service Scope — service boundary
- Communication Domain Model — entity definitions
- Contact Intelligence Architecture — consumer of inbound replies
- AI Brain Architecture — consumer of personalization signals
- Current implementation:
apps/communication/src/index.ts,apps/newsletter-service/src/lib/campaign-send.ts
External
- AWS SES v2 API: https://docs.aws.amazon.com/ses/latest/APIReference-V2/
- Cloudflare Email Routing: https://developers.cloudflare.com/email-routing/
- Cloudflare Email Workers: https://developers.cloudflare.com/email-routing/email-workers/
- Cloudflare Vectorize: https://developers.cloudflare.com/vectorize/
- Workers AI: https://developers.cloudflare.com/workers-ai/
- Cloudflare DNS API: https://developers.cloudflare.com/api/operations/dns-records-for-a-zone-list-dns-records
- DMARC primer: https://dmarc.org/
- Gmail bulk-sender requirements: https://support.google.com/mail/answer/81126