SMTP and Email Configuration
To enable email features such as password recovery and new account notifications, you must configure SMTP credentials in your environment variables. The system automatically detects these settings and enables email functionality across the application.
Configure SMTP Credentials
Add the following variables to your .env file located in the project root. The Settings class in backend/app/core/config.py reads these values to initialize the application.
# .env
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_TLS=True
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-password
EMAILS_FROM_EMAIL=your-email@gmail.com
EMAILS_FROM_NAME="My Project Name"
Automatic Activation Logic
The application uses a computed property in the Settings class to determine if email features should be active. Email functionality is enabled only if both SMTP_HOST and EMAILS_FROM_EMAIL are provided.
# backend/app/core/config.py
@computed_field
@property
def emails_enabled(self) -> bool:
return bool(self.SMTP_HOST and self.EMAILS_FROM_EMAIL)
Test the Configuration
Once you have configured the environment variables, you can verify the setup using the built-in test endpoint. This requires superuser authentication.
Endpoint: POST /api/v1/utils/test-email/
You can call this via the Swagger UI or using curl:
curl -X 'POST' \
'http://localhost/api/v1/utils/test-email/?email_to=test@example.com' \
-H 'accept: application/json' \
-H 'Authorization: Bearer <YOUR_SUPERUSER_TOKEN>'
This endpoint uses the send_email utility and the generate_test_email helper found in backend/app/utils.py.
Usage in the Codebase
The application uses settings.emails_enabled to conditionally trigger email logic.
New User Notifications
When a superuser creates a new user, the system checks if emails are enabled before attempting to send the welcome email.
# backend/app/api/routes/users.py
@router.post("/", dependencies=[Depends(get_current_active_superuser)], response_model=UserPublic)
def create_user(*, session: SessionDep, user_in: UserCreate) -> Any:
# ... user creation logic ...
user = crud.create_user(session=session, user_create=user_in)
if settings.emails_enabled and user_in.email:
email_data = generate_new_account_email(
email_to=user_in.email, username=user_in.email, password=user_in.password
)
send_email(
email_to=user_in.email,
subject=email_data.subject,
html_content=email_data.html_content,
)
return user
Password Recovery
The password recovery flow in backend/app/api/routes/login.py calls send_email directly. Note that send_email contains an internal assertion that will fail if the configuration is missing.
# backend/app/utils.py
def send_email(*, email_to: str, subject: str = "", html_content: str = "") -> None:
assert settings.emails_enabled, "no provided configuration for email variables"
# ... SMTP connection and sending logic ...
Troubleshooting
Missing Configuration
If SMTP_HOST or EMAILS_FROM_EMAIL are missing, settings.emails_enabled will be False. In this state:
- The
/test-email/endpoint will raise anAssertionError. - New user welcome emails will be silently skipped.
- Password recovery attempts will fail with an
AssertionError.
Template Not Found
The render_email_template function in backend/app/utils.py expects compiled HTML templates to exist in backend/app/email-templates/build/.
# backend/app/utils.py
def render_email_template(*, template_name: str, context: dict[str, Any]) -> str:
template_str = (
Path(__file__).parent / "email-templates" / "build" / template_name
).read_text()
# ...
If you receive a FileNotFoundError, ensure that the email templates have been built (typically using the MJML workflow provided in the email-templates directory).
Connection Issues
The send_email utility supports both TLS and SSL. By default, SMTP_TLS is True. If your provider requires SSL (often on port 465), update your .env:
SMTP_TLS=False
SMTP_SSL=True
SMTP_PORT=465