diff --git a/backend/app/core/email.py b/backend/app/core/email.py
index 908715eb..5a5defae 100644
--- a/backend/app/core/email.py
+++ b/backend/app/core/email.py
@@ -283,6 +283,144 @@ class EmailService:
return False
+ @staticmethod
+ async def send_survey_notification_email(
+ to_email: str,
+ respondent_name: str | None,
+ responses: dict,
+ ) -> bool:
+ """Send survey response notification. Fire-and-forget."""
+ if not settings.email_enabled:
+ logger.warning("Email not sent — RESEND_API_KEY not configured")
+ return False
+
+ try:
+ import resend
+ import html as html_mod
+ from datetime import datetime, timezone
+
+ resend.api_key = settings.RESEND_API_KEY
+
+ name_display = html_mod.escape(respondent_name) if respondent_name else "Anonymous"
+ date_str = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
+ subject = f"[FlowPilot Survey] New Response from {name_display} — {date_str}"
+
+ rows_html = ""
+ for q_id, answer in responses.items():
+ safe_id = html_mod.escape(str(q_id))
+ if isinstance(answer, list):
+ answer_display = "
".join(f"• {html_mod.escape(str(item))}" for item in answer)
+ else:
+ answer_display = html_mod.escape(str(answer))
+ rows_html += f"""
+
+ | {safe_id} |
+ {answer_display} |
+
"""
+
+ email_html = f"""
+
+
+
+
+
+
+ FlowPilot Survey Response
+ From: {name_display} • {date_str}
+ |
+ |
+
+ |
+ |
+ This response has been saved to the survey_responses table.
+ |
+
+ |
+
+"""
+
+ resend.Emails.send({
+ "from": settings.FROM_EMAIL,
+ "to": [to_email],
+ "subject": subject,
+ "html": email_html,
+ })
+ logger.info("Survey notification sent for respondent: %s", name_display)
+ return True
+
+ except Exception:
+ logger.exception("Failed to send survey notification email")
+ return False
+
+ @staticmethod
+ async def send_survey_invite_email(
+ to_email: str,
+ recipient_name: str,
+ survey_url: str,
+ ) -> bool:
+ """Send survey invite email."""
+ if not settings.email_enabled:
+ logger.warning("Email not sent — RESEND_API_KEY not configured")
+ return False
+
+ try:
+ import resend
+ import html as html_mod
+
+ resend.api_key = settings.RESEND_API_KEY
+
+ safe_name = html_mod.escape(recipient_name)
+ subject = "FlowPilot Survey — Your expertise matters"
+
+ email_html = f"""
+
+
+
+
+
+
+ ResolutionFlow
+ FlowPilot Research
+ |
+ |
+
+ Hi {safe_name},
+
+
+ We're building an AI assistant for MSP engineers and would love your input. This 5-minute survey will help shape how FlowPilot thinks about troubleshooting.
+
+ |
+ |
+
+ Take the Survey
+
+ |
+ |
+
+ Your responses are confidential. Takes about 5 minutes.
+
+ |
+
+ |
+
+"""
+
+ resend.Emails.send({
+ "from": settings.FROM_EMAIL,
+ "to": [to_email],
+ "subject": subject,
+ "html": email_html,
+ })
+ logger.info("Survey invite email sent to %s", to_email)
+ return True
+
+ except Exception:
+ logger.exception("Failed to send survey invite email to %s", to_email)
+ return False
+
+
def _render_invite_html(
code: str,
plan_label: str,