Skip to Content
ContributeBackendEmail Notifications

Email Notification System

The worker emails users when selected Celery tasks finish. Only tasks decorated with @email_notification send mail, so parallel subtasks stay silent and a run produces one summary email rather than many. Sending is non-blocking, runs on both success and permanent failure, and no-ops if SMTP is not configured.

Configuration

Set these in the worker deployment. Any SMTP provider works; the example is SendGrid:

.env
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey            # literal "apikey" for SendGrid
SMTP_PASSWORD=your_api_key

FRONTEND_URL=https://app.rhesis.ai   # base URL for result links

If any SMTP variable is missing, EmailService.is_configured is false and notifications are skipped with a log line rather than an error.

Opting a task in

Decorate a task with @email_notification, choosing a template. Without the decorator, no mail is sent.

task.py
from rhesis.backend.tasks.base import BaseTask, with_tenant_context, email_notification
from rhesis.backend.notifications 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):
    return {"result": "success", "test_run_id": "optional-for-links"}

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

Current usage:

  • collect_resultsTEST_EXECUTION_SUMMARY
  • email_notification_testTASK_COMPLETION
  • per-test execution and utility tasks — no decorator

Parallel test execution runs an async batch inside one Celery task (not a Celery chord). When the batch finishes, trigger_results_collection schedules collect_results with the result list, so the summary email path is the same as for sequential runs. See tasks/execution/shared.py and tasks/execution/results.py.

Template variables

The decorator provides these to every template:

  • recipient_name — user’s display name
  • task_name — human-readable task name
  • task_id
  • statussuccess or failed
  • execution_time — formatted duration
  • error_message — for failed tasks
  • frontend_url
  • completed_at

Anything the task returns is merged in, so a summary template can use extra fields:

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

How it works

EmailService (notifications/email/service.py, exported as the email_service singleton from rhesis.backend.notifications) loads SMTP config from the environment and renders Jinja2 templates. On success or permanent failure, BaseTask (in its on_success / on_failure hooks) loads the task owner from the database, renders the template with the task’s return values and timing, and calls email_service.send_email(...). Users with placeholder addresses (*@placeholder.rhesis.ai) are skipped, and send errors are logged without failing the task.

Troubleshooting

  • No emails — check worker logs for SMTP configuration incomplete, confirm the SMTP_* variables reach the worker pods, and verify the recipient has a real (non-placeholder) address.
  • Auth errors — recheck SMTP_USER/SMTP_PASSWORD (SendGrid uses the literal apikey as the username) and that port 587 is reachable from the worker.
  • Missing result links — set FRONTEND_URL and return test_run_id from the task.

Security

Emails go only to the task owner, and multi-tenant scoping prevents cross-organization delivery. Bodies carry status and timing, not task result data. SMTP connections use STARTTLS and credentials are stored as worker secrets.