feat(email): replace Resend with Cloudflare Email Service#150
Merged
Conversation
Route all transactional email (onboarding sequence + weekly digest)
through the Cloudflare Email Service `EMAIL` worker binding instead of
Resend's HTTP API.
- EmailService resolves the `send_email` binding from WorkerEnvironment
and calls env.EMAIL.send({ from, to, subject, html, replyTo }); the
public send() signature is unchanged so onboarding/digest logic and
email templates are untouched. No API key/secret needed.
- Env: drop RESEND_API_KEY/RESEND_FROM_EMAIL, add EMAIL_FROM.
- alchemy + wrangler: add EmailSender/send_email binding for api +
alerting, swap RESEND_FROM_EMAIL -> EMAIL_FROM, remove RESEND secret.
- Sender identity unchanged: From noreply.maple.dev, Reply-To
david@maple.dev on the founder/onboarding emails.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
|
Your LLM provider API key was rejected. Rotate the key in your provider dashboard, then update the matching GitHub Actions secret. Update repo secret → · Model settings → · Setup docs → · Ask in Discord →
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What
Replaces Resend with Cloudflare Email Service ("Email Sending", public beta) for all transactional email — the onboarding sequence and the weekly digest. The Cloudflare account already has
noreply.maple.devonboarded (DNS configured, enabled, 1000/day quota), so delivery now goes through the nativeEMAILworker binding — no API key/secret.Why
Drop the external Resend dependency (API key secret + raw HTTP call) in favor of the platform-native sender, with zero changes to email content or send-site logic.
Changes
apps/api/src/lib/EmailService.ts— resolves thesend_emailbinding fromWorkerEnvironmentand callsenv.EMAIL.send({ from, to, subject, html, replyTo }). The publicsend(to, subject, html, replyTo?)signature andEmailDeliveryErrorare unchanged, soOnboardingEmailService/DigestServiceand the React Email templates are untouched.isConfigurednow reflects binding presence; 15s timeout, PII rules, and logging preserved; spanemail.provider="cloudflare"+email.message_id.apps/api/src/lib/Env.ts— dropRESEND_API_KEY/RESEND_FROM_EMAIL, addEMAIL_FROM(defaultMaple <notifications@noreply.maple.dev>).EmailSender/send_emailbinding, swapRESEND_FROM_EMAIL→EMAIL_FROM, remove theRESEND_API_KEYsecret.apps/alerting/src/worker.ts— feedWorkerEnvironment.layerintoEmailServiceLive(the api worker already provides it at app scope).OnboardingEmailService.ts,.env.local,README.md.Sender identity unchanged: From
noreply.maple.dev, Reply-Todavid@maple.devon the founder/onboarding emails only.Verification
bun typecheck— all 24 packages pass.bun run --cwd apps/api test— 631/631 pass. Outside a worker isolateEMAILis undefined →isConfigured=false, sends skip (no regression).Follow-up (needs a deploy)
EMAILbinding attaches, fire one real send, and check the Email Sending dashboard counter + delivery. Watch forE_SENDER_NOT_VERIFIED(would meanEMAIL_FROM/allowedSenderAddressesdon't match the onboardednoreply.maple.devdomain).remote: true, sowrangler devsends through the real Cloudflare sender; purebun devstays a no-op.🤖 Generated with Claude Code