diff --git a/backend/app/api/endpoints/beta_signup.py b/backend/app/api/endpoints/beta_signup.py new file mode 100644 index 00000000..d4b2c7bc --- /dev/null +++ b/backend/app/api/endpoints/beta_signup.py @@ -0,0 +1,31 @@ +"""Public beta signup endpoint — no auth required.""" + +import logging +from fastapi import APIRouter, HTTPException +from pydantic import BaseModel, EmailStr +from app.core.email import EmailService + +logger = logging.getLogger(__name__) + +router = APIRouter(prefix="/beta-signup", tags=["beta"]) + + +class BetaSignupRequest(BaseModel): + email: EmailStr + + +class BetaSignupResponse(BaseModel): + success: bool + message: str + + +@router.post("", response_model=BetaSignupResponse) +async def beta_signup(data: BetaSignupRequest): + """Collect beta interest — sends notification to beta@resolutionflow.com.""" + sent = await EmailService.send_beta_signup_notification(data.email) + if not sent: + logger.warning("Beta signup recorded (email delivery skipped): %s", data.email) + return BetaSignupResponse( + success=True, + message="Thanks! We'll be in touch with beta access details.", + ) diff --git a/backend/app/api/router.py b/backend/app/api/router.py index b5951dde..5e789ff9 100644 --- a/backend/app/api/router.py +++ b/backend/app/api/router.py @@ -15,6 +15,7 @@ from app.api.endpoints import admin_survey from app.api.endpoints import tree_transfer from app.api.endpoints import ai_suggestions from app.api.endpoints import kb_accelerator +from app.api.endpoints import beta_signup api_router = APIRouter() @@ -54,3 +55,4 @@ api_router.include_router(admin_survey.router) api_router.include_router(tree_transfer.router) api_router.include_router(ai_suggestions.router) api_router.include_router(kb_accelerator.router) +api_router.include_router(beta_signup.router) diff --git a/backend/app/core/ai_tree_validator.py b/backend/app/core/ai_tree_validator.py index 850aa219..97cc9d78 100644 --- a/backend/app/core/ai_tree_validator.py +++ b/backend/app/core/ai_tree_validator.py @@ -151,9 +151,9 @@ def validate_generated_tree(tree: dict[str, Any]) -> list[str]: errors.append( f"Tree has only {node_count} nodes. Minimum 5 required for a useful tree." ) - if node_count > 50: + if node_count > 100: errors.append( - f"Tree has {node_count} nodes. Maximum 50 allowed." + f"Tree has {node_count} nodes. Maximum 100 allowed." ) if solution_count < 2: errors.append( diff --git a/backend/app/core/email.py b/backend/app/core/email.py index bdea462f..24c13dcb 100644 --- a/backend/app/core/email.py +++ b/backend/app/core/email.py @@ -418,6 +418,72 @@ class EmailService: logger.exception("Failed to send survey copy email to %s", to_email) return False + @staticmethod + async def send_beta_signup_notification( + signup_email: str, + notify_email: str = "beta@resolutionflow.com", + ) -> bool: + """Notify beta@resolutionflow.com about a new beta signup. Fire-and-forget.""" + if not settings.email_enabled: + logger.warning("Beta signup 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 + + date_str = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC") + safe_email = html_mod.escape(signup_email) + subject = f"[ResolutionFlow Beta] New signup — {safe_email}" + + email_html = f""" +
+ +
+
|
+ AI-guided decision trees that walk your engineers through troubleshooting — and automatically document every step, ready for your PSA ticket. +
+Built by MSP engineers, for MSP engineers
++ What if documentation was a byproduct of solving the issue — not a separate task? What if your engineers never had to write another ticket note? +
+Use the visual Flow Editor to create branching decision trees for any troubleshooting scenario. Drag, connect, and enrich steps with commands, notes, and AI suggestions.
+An engineer launches the flow on a live ticket. FlowPilot — your AI copilot — acts as a virtual senior engineer, guiding decisions and capturing every action in real time.
+When the session ends, full documentation is generated — formatted for your PSA. Paste it directly into ConnectWise, Atera, or Syncro. Done.
++ Need Enterprise (25+ techs, SSO, custom branding)?{' '} + Contact us +
+Join the beta and see what happens when documentation becomes automatic.
+ + {betaStatus === 'sent' && ( +Thanks! We'll be in touch with beta access details.
+ )} + {betaStatus === 'error' && ( +Something went wrong. Please try again.
+ )} +Free to start. No credit card required.
+{description}
+{description}
+