SOP: Email Send Workflow

sop-email-send Sop

sop active

SOP: Email Send Workflow

Purpose

Standard process for composing and sending branded emails to Westside parents. Used by agents (Ava, Penny, dev agents) and humans (Lucas, Marcus). Applies to all email sends — blasts, transactional, and one-off. Produces a sent email with EmailLog audit trail, or a clear "not approved" stop. The key outcome is that new emails can be sent without code changes when the layout and audience query already exist.

Steps

  • Intake. Requester (Marcus or Lucas) describes email intent. Composer (agent) determines:
  • Draft content. Composer writes the email data dict and presents it to Approver (Lucas) in the conversation. Include: layout, email_type, query, subject (with per-recipient {{placeholders}}), data dict (headline, body, cta_text, cta_url, footer_note). Do NOT call any endpoint yet.
  • Approver reviews draft text. Approver says "looks good" or requests changes. If changes requested, revise and re-present. Loop until text is approved.
  • Send test email. Call POST /email/blast with the draft payload plus "test_email": "draneylucas@gmail.com". Report: "Test email sent to draneylucas@gmail.com. Check your phone."
  • STOP — wait for approval. Do NOT proceed. Approver checks email on their phone (real Gmail renderer, real mobile viewport). Approver responds:
  • Confirm blast scope. Query the audience count: report "Sending to N parents. Confirm?" Wait for explicit "send it" or "yes" from Approver.
  • Blast. Call POST /email/blast without test_email param. Report results: "Sent N emails. M errors: [details if any]."
  • Verify. Confirm EmailLog entries via database query or admin endpoint. Report send count matches expected audience.

Contract Emails (Enhanced Gate)

Contract-related emails (contract_offer, contract_reminder) require double approval:
  • Steps 1-5 as above (first approval after test email on phone).
  • Approver says "approved" after checking test on phone.
  • Show recipient list and count. Approver says "send it" (second approval).
  • Send ONE real email to a single parent (not test_email — a real recipient). Approver verifies in that parent's Gmail if possible, or confirms intent.
  • Approver says "yes, blast the rest" (third confirmation). Then blast.

New Email Type Checklist

If the email needs infrastructure that doesn't exist:
  • New audience query needed? → Create Forgejo issue + board item for email_queries.py change.
  • New EmailType enum value needed? → Create Forgejo issue + board item for alembic migration.
  • New layout needed? → Create Forgejo issue + board item for MJML template + compile.
  • All three exist? → No code needed. Proceed directly to Step 1.

Rules

  • NEVER send to real recipients without explicit approval in the current conversation. Approval from a previous session does not carry over.
  • ONE APPROVAL = ONE SEND. Every individual send (test or blast) requires its own explicit 'send it' / 'yes' in the conversation immediately before that send. Prior approvals do NOT authorize subsequent sends, even minutes apart. A new tool call = a new send = a new approval required.
  • CONTENT CHANGES DO NOT AUTHORIZE A SEND. 'Change the date to Friday' or 'fix the wording' is instruction to modify the draft, NOT to send it. After applying the change, re-present the draft and wait for explicit send approval. 2026-04-13 incident: one approval was treated as a blanket, resulting in 3 unapproved sends to a real recipient.
  • NEVER skip the test email step. Every email send starts with test_email=draneylucas@gmail.com.
  • NEVER create audience queries or layouts inline during a send workflow. Missing infrastructure = code ticket first.
  • NEVER assume approval. 'Looks good' on the draft text is NOT approval to blast. The phone check (Step 5) is the gate.
  • When in doubt, ask. The cost of asking 'send this now?' is 2 seconds. The cost of an unapproved send is trust erosion and potential inbox contamination for real recipients.
  • Contract emails require double approval — see enhanced gate above.
  • Test email address: draneylucas@gmail.com (Lucas's personal Gmail for mobile verification). For Marcus-facing tests, use marcusdraney23@gmail.com ONLY with explicit approval for each send.
  • The browser is not the truth. Gmail's renderer is proprietary. Only real emails on real phones count as verification.
  • EmailLog is automatic — do not skip or manually log. Every send_templated_email() call writes to email_log.
  • arch-email — architecture reference: components, layouts, preview workflow, design decisions
  • project-westside-basketball — parent project
  • sop-board-workflow — board workflow for creating tickets when infrastructure is missing