Skip to Content
ContributeBackendEmail Notifications

Email Notification System

Overview

The worker can email users when selected Celery tasks finish. SMTP is pluggable (SendGrid and others). Sending is non-blocking and respects tenant context.

Features

  • Optional per-task via @email_notification (avoids noisy mail when many parallel tasks run)
  • HTML and plain text bodies with links into the app when results include IDs
  • Graceful no-op if SMTP is misconfigured

Configuration

Environment Variables

The following environment variables must be configured in the worker deployment:

.env
# SMTP Configuration (e.g., SendGrid)
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASSWORD=your_sendgrid_api_key

# Frontend URL for generating links
FRONTEND_URL=https://app.rhesis.ai

SendGrid Setup

For SendGrid specifically:

  1. Create a SendGrid account and verify your sender domain
  2. Generate an API key with “Mail Send” permissions
  3. Use these settings:
    • SMTP_HOST: smtp.sendgrid.net
    • SMTP_PORT: 587
    • SMTP_USER: apikey
    • SMTP_PASSWORD: Your SendGrid API key

Other SMTP Providers

The system works with any SMTP provider. Common configurations:

smtp-providers.env
# Gmail SMTP
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-password

# AWS SES
SMTP_HOST=email-smtp.us-east-1.amazonaws.com
SMTP_PORT=587
SMTP_USER=your-ses-smtp-username
SMTP_PASSWORD=your-ses-smtp-password

# Mailgun
SMTP_HOST=smtp.mailgun.org
SMTP_PORT=587
SMTP_USER=your-mailgun-smtp-login
SMTP_PASSWORD=your-mailgun-smtp-password

How It Works

Opt-in: @email_notification

Only tasks decorated with @email_notification send mail (parallel subtasks usually stay silent; use a chord callback for one summary email).

email-notification-decorator.py
from rhesis.backend.tasks.base import BaseTask, with_tenant_context, email_notification
from rhesis.backend.notifications.email.template_service import EmailTemplate
from rhesis.backend.worker import app

@email_notification(
    template=EmailTemplate.TASK_COMPLETION,
    subject_template="Task Complete: {task_name} - {status.title()}"
)
@app.task(base=BaseTask, name="your.user.facing.task", bind=True)
@with_tenant_context
def user_facing_task(self, params, db=None):
    # Your task logic here
    return {"result": "success", "test_run_id": "optional-for-links"}

Templates: EmailTemplate.TASK_COMPLETION (generic) or EmailTemplate.TEST_EXECUTION_SUMMARY (runs). Optional subject_template= uses the same context variables.

Without the decorator: no notification. Legacy: EmailEnabledTask still sends basic completion emails.

silent-task.py
from rhesis.backend.tasks.base import BaseTask, with_tenant_context
from rhesis.backend.worker import app

@app.task(base=BaseTask, name="your.background.task", bind=True)
@with_tenant_context
def background_task(self, params, db=None):
    return {"result": "success"}

Async batch and summary email

Parallel test execution uses an async batch inside one Celery task; it does not use Celery chord. When the batch finishes, trigger_results_collection schedules collect_results with the result list so the same summary path (including @email_notification) runs as for sequential runs. See apps/backend/src/rhesis/backend/tasks/execution/shared.py and batch/__init__.py.

Current Task Configuration

  • collect_resultsTEST_EXECUTION_SUMMARY
  • email_notification_testTASK_COMPLETION
  • Per-test execution and utility tasks — no decorator (no mail)

Automatic Integration

On success or final failure, the worker loads the user from task context (skips placeholder addresses), renders the template with task return values and timing, sends via EmailService, and logs errors without failing the task.

Template Variables

The decorator automatically provides these variables to templates:

  • recipient_name: User’s display name
  • task_name: Human-readable task name
  • task_id: Unique task identifier
  • status: Task completion status (‘success’ or ‘failed’)
  • execution_time: Formatted execution duration
  • error_message: Error details (for failed tasks)
  • frontend_url: Base URL for links
  • completed_at: Completion timestamp

Additional variables can be provided by returning them from the task:

template-variables.py
@email_notification(template=EmailTemplate.TEST_EXECUTION_SUMMARY)
@app.task(base=BaseTask, bind=True)
def test_task(self):
    # Task logic here
    return {
        'total_tests': 10,
        'tests_passed': 8,
        'tests_failed': 2,
        'test_set_name': 'API Tests',
        'project_name': 'My Project'
    }

Email Service Architecture

EmailService Class

The core EmailService class in tasks/email_service.py handles:

email-service.py
class EmailService:
    def __init__(self):
        # Loads SMTP configuration from environment
        # Validates configuration completeness

    def send_task_completion_email(
        self,
        recipient_email: str,
        recipient_name: Optional[str],
        task_name: str,
        task_id: str,
        status: str,
        execution_time: Optional[str] = None,
        error_message: Optional[str] = None,
        test_run_id: Optional[str] = None,
        frontend_url: Optional[str] = None
    ) -> bool:
        # Sends HTML and plain text email
        # Returns success/failure status

BaseTask Integration

The BaseTask class includes:

  • _get_user_info(): Retrieves user email and name from database
  • _send_task_completion_email(): Handles email sending with error handling
  • on_success(): Sends success notifications
  • on_failure(): Sends failure notifications (only for permanent failures)

User Experience

What Users Receive

When users submit tasks through the API (e.g., test configurations), they automatically receive:

  1. Immediate API response with task ID
  2. Email notification when the task completes with:
    • Clear status indication
    • Task details and timing
    • Direct link to results (if applicable)
    • Professional branding

Email Examples

Success Email

success-email-example.txt
Subject: Task Completed: Execute Test Configuration - Success

Task completed

Hello John Doe,

Your task has completed with status: SUCCESS

Task Details:
- Task Name: Execute Test Configuration
- Task ID: 12345678-1234-5678-9012-123456789012
- Status: Success
- Completed at: 2024-01-15 14:30:25 UTC
- Execution Time: 2m 45s
- Test Run ID: abcd1234-5678-9012-efgh-567890123456

[View Results]

Best regards,
Rhesis AI Team

Testing

Test Endpoint

Use the test endpoint to verify email functionality:

test-endpoint.sh
POST /api/tasks/email-notification-test
Authorization: Bearer <your-token>
Content-Type: application/json

{
"message": "Testing email notifications"
}

Manual Testing

manual-testing.py
# In your development environment
from rhesis.backend.tasks.email_service import email_service

success = email_service.send_task_completion_email(
    recipient_email="test@example.com",
    recipient_name="Test User",
    task_name="Test Task",
    task_id="test-123",
    status="success",
    frontend_url="https://app.rhesis.ai"
)
print(f"Email sent: {success}")

Troubleshooting

Common Issues

No Emails Being Sent

  1. Check SMTP configuration:
check-smtp-logs.sh
# In worker logs, look for:
"SMTP configuration incomplete. Email notifications will be disabled."
  1. Verify environment variables:
verify-env-vars.sh
kubectl exec -it deployment/rhesis-worker -- env | grep SMTP
  1. Check user emails:
    • Ensure users have valid email addresses
    • System skips placeholder emails (*@placeholder.rhesis.ai)

SMTP Authentication Errors

  1. Verify credentials:

    • Double-check SMTP username and password
    • For SendGrid, ensure you’re using apikey as username
  2. Check firewall/network:

    • Ensure port 587 is accessible from worker pods
    • Some networks block SMTP ports

Email Content Issues

  1. Missing links:

    • Verify FRONTEND_URL is set correctly
    • Check that test_run_id is included in task results
  2. Formatting problems:

    • Check logs for email generation errors
    • Verify HTML content in email client

Monitoring

Monitor email notifications through:

  1. Worker logs:
worker-logs.sh
kubectl logs deployment/rhesis-worker | grep "email"
  1. Success indicators:
success-indicator.txt
Task completion email sent successfully to user@example.com for task 12345
  1. Error indicators:
error-indicator.txt
Failed to send task completion email to user@example.com: SMTP error

Security Considerations

Email Content

  • No sensitive data: Task results are not included in emails
  • Secure links: Frontend URLs use HTTPS
  • User privacy: Only the task owner receives notifications

SMTP Security

  • TLS encryption: All SMTP connections use STARTTLS
  • Credential protection: SMTP passwords stored as Kubernetes secrets
  • Network security: SMTP traffic encrypted in transit

Access Control

  • User context: Emails only sent to the user who submitted the task
  • Organization isolation: Multi-tenant architecture prevents cross-organization emails

Deployment

Worker Deployment

Ensure your worker deployment includes the SMTP environment variables:

deployment.yaml
# apps/worker/k8s/deployment.yaml
env:
- name: SMTP_HOST
    valueFrom:
      secretKeyRef:
        name: rhesis-worker-secrets
        key: SMTP_HOST
- name: SMTP_PORT
    valueFrom:
      secretKeyRef:
        name: rhesis-worker-secrets
        key: SMTP_PORT
- name: SMTP_USER
    valueFrom:
      secretKeyRef:
        name: rhesis-worker-secrets
        key: SMTP_USER
- name: SMTP_PASSWORD
    valueFrom:
      secretKeyRef:
        name: rhesis-worker-secrets
        key: SMTP_PASSWORD
- name: FRONTEND_URL
    valueFrom:
      secretKeyRef:
        name: rhesis-worker-secrets
        key: FRONTEND_URL

CI/CD Pipeline

The GitHub Actions workflow automatically includes SMTP secrets:

worker-workflow.yaml
# .github/workflows/worker.yml
--from-literal=SMTP_HOST="${{ secrets.SMTP_HOST }}" \
--from-literal=SMTP_PORT="${{ secrets.SMTP_PORT }}" \
--from-literal=SMTP_USER="${{ secrets.SMTP_USER }}" \
--from-literal=SMTP_PASSWORD="${{ secrets.SMTP_PASSWORD }}" \
--from-literal=FRONTEND_URL="${{ secrets.FRONTEND_URL }}" \

Remember to set these secrets in your GitHub repository settings for each environment (dev, stg, prd).