Session 14: Azure Email Setup, Admin Access Fix, Edit Request Notifications
Date: March 18, 2026 Focus: ACS email infrastructure, Sheri admin access fix, edit request email notifications, book status guards
Work Completed
1. Fix Sheri's Admin Access (0.5 SP)
What Changed:
- Sheri's profile (
sheri@sayitnowbook.com) hadrole: "user"in the Supabaseprofilestable - Updated to
role: "admin"so she can access/signaland/admin
Technical Details: Direct update via Supabase management console. No migration required — role column already existed with correct RLS policies.
2. Azure Communication Services Email Setup (2 SP)
What Changed:
- Created dedicated Resource Group
rg-sayitnowin ATH Azure subscription - Provisioned ACS resource
acs-sayitnowand email serviceemail-sayitnow - Configured custom domain
sayitnowbook.com— fully verified (Domain, SPF, DKIM, DKIM2) - Created sender address
noreply@sayitnowbook.comwith display name "Say It Now" - Added DNS records in Cloudflare: TXT (domain verification), merged SPF, CNAME (DKIM x2)
- Merged SPF record:
v=spf1 include:_spf.mx.cloudflare.net include:spf.protection.outlook.com -all - Installed
@azure/communication-emailnpm package - Added
ACS_CONNECTION_STRINGenvironment variable to Netlify
Technical Details: ACS email uses a connection string for authentication. Custom domain verification requires four DNS records in Cloudflare: one TXT for domain ownership, one TXT for SPF (merged with existing Cloudflare email routing SPF), and two CNAMEs for DKIM signing. The SPF records were merged into a single TXT record since DNS only allows one SPF record per domain.
End-to-end test passed: email from noreply@sayitnowbook.com → bert@askthehuman.com received successfully.
Files Modified:
package.json/package-lock.json— Added@azure/communication-emaildependency
3. Edit Request Email Notification System (3 SP)
What Changed:
- New
src/lib/email.ts— ACS email utility withsendEditRequestEmail()function. Sends styled HTML email to contributor with feedback from book owner and a call-to-action button linking to/b/[share_code]. - New
src/pages/api/request-edit.ts— API route that verifies auth and book ownership, updates submission status toneeds_edit, and triggers the email - Modified
src/pages/dashboard/[bookId].astro—submitEditRequest()now calls the API route instead of updating the DB directly from the client
New Files:
src/lib/email.ts— ACS email client, HTML email template,sendEditRequestEmail()src/pages/api/request-edit.ts— Auth-gated API route for edit requests
Modified Files:
src/pages/dashboard/[bookId].astro— Edit request flow wired to API route
Technical Details:
The dashboard previously updated the submissions table directly via the Supabase JS client. This was moved to a server-side API route so that the email send happens atomically with the DB update — both fail or both succeed. The API route validates that the requesting user owns the book before allowing the status update, guarding against IDOR.
4. Book Status Guards for Edit Requests (1 SP)
What Changed:
src/pages/b/[code].astro— Contributor form now blocks books with statuscomplete,published, orarchived(previously only blockedarchived)src/pages/api/request-edit.ts— API rejects edit requests for finalized books (complete,published,archived)
Modified Files:
src/pages/b/[code].astro— Expanded status guardsrc/pages/api/request-edit.ts— Server-side guard included at API level
Technical Details:
The guard exists in two places intentionally: the contributor form shows a user-facing "this book is closed" message, while the API guard is the authoritative enforcement layer. Books that are complete or published should not accept new contributions or re-open submissions for editing.
Decisions Made
- ACS over Google/Gmail API — Keep email infrastructure on Azure. Easier to hand off to Sheri's own Azure subscription later. Connection string auth is simpler than OAuth2.
- Dedicated Resource Group (
rg-sayitnow) — Isolated from other ATH resources; can be moved to Sheri's subscription cleanly when she's ready to own her infrastructure. - ATH Azure subscription — Not Recovery Ecosystem. Right tenant for this project.
noreply@sayitnowbook.comas sender — Professional address matching the domain, "Say It Now" display name.- Merged SPF record — DNS only allows one SPF
TXTrecord per domain. Cloudflare email routing (_spf.mx.cloudflare.net) and ACS (spf.protection.outlook.com) were combined into one. - Books in
completeorpublishedstatus cannot accept edits — Once an owner finalizes, the contributor form is closed and the API blocks edit requests. - Edit request email notification ships in MVP (not deferred to Phase 10) — The gap was small enough to close now. Improves contributor experience meaningfully and removes a known friction point before beta round 2.
- No Resend, Postmark, or Brevo — ACS or Google only.
Security Considerations
- API route validates session JWT before processing any DB writes
- Ownership check prevents any authenticated user from requesting edits on books they don't own (IDOR guard)
ACS_CONNECTION_STRINGstored as Netlify environment variable, not in source code- Status guard enforced server-side on API; client-side guard is UX-only
Testing
Manual Testing:
- Test email sent from
noreply@sayitnowbook.comtobert@askthehuman.com— received successfully - Sheri admin access verified (able to reach
/signaland/admin) - Edit request flow tested end-to-end: owner requests edit → submission status updates to
needs_edit→ email delivered to contributor
Blockers & Challenges
Challenge: SPF Record Merge
Issue: Cloudflare already had an SPF TXT record for email routing. ACS requires adding its own SPF include. DNS only allows one SPF record per domain.
Resolution: Merged both includes into a single record: v=spf1 include:_spf.mx.cloudflare.net include:spf.protection.outlook.com -all
Next Steps
- Sheri re-tests full flow for beta round 2 (highest priority)
- Wire cover design into public preview page (
/preview/[code].astro) - Add Lulu
pod_package_idvalues tobook-sizes.tsfor API integration - Integrate Lulu print-job-cost-calculations API for real-time pricing
- Build print order checkout flow (Stripe → Lulu API)
- Explore email invitation flow (Phase 10 — owner invites contributors via email)
File Changes Summary
New: 2 files (src/lib/email.ts, src/pages/api/request-edit.ts)
Modified: 3 files (src/pages/dashboard/[bookId].astro, src/pages/b/[code].astro, package.json)
Generated with Claude Code Co-Authored-By: Claude noreply@anthropic.com