Building Email Notifications and Daily Tender Opportunity Digests

Introduction

The AI Tender Opportunity Scanner now provides a complete in-application workflow:

  • Discover tender opportunities
  • Analyze opportunities using AI
  • Match opportunities to business profiles
  • Manage bid pipelines
  • Monitor deadlines and internal actions
  • Generate notifications when important events occur

Users now receive notifications inside the application.

However, users do not spend their entire day inside the AI Tender Opportunity Scanner.

Most business users spend their working day in:

  • Email
  • Microsoft Teams
  • Slack
  • Outlook
  • CRM systems

If an important tender deadline occurs while the application is closed, an in-app notification alone is insufficient.

The platform must actively deliver important information to users where they already work.

In this article we will build a complete email notification system capable of sending:

  • Immediate high-priority alerts
  • Tender deadline reminders
  • Internal action reminders
  • Daily opportunity digests
  • Weekly pipeline summaries
  • Email preference management
  • Delivery tracking
  • Retry handling
  • Email templates

This transforms the notification system into a complete communication platform.


Development Progress

Project Scope and Roadmap ████████████████████ 100%
MVP Definition ████████████████████ 100%
Technology Stack ████████████████████ 100%
System Architecture ████████████████████ 100%
Database Design ████████████████████ 100%
Backend Project Structure ████████████████████ 100%
SQLAlchemy Models ████████████████████ 100%
Alembic Migration ████████████████████ 100%
Repository Layer ████████████████████ 100%
CRUD Service Layer ████████████████████ 100%
Tender Ingestion Pipeline ████████████████████ 100%
AI Analysis Pipeline ████████████████████ 100%
AI Matching Engine ████████████████████ 100%
React Dashboard ████████████████████ 100%
Tender Detail Page ████████████████████ 100%
Saved Opportunities Workspace ████████████████████ 100%
Pipeline Analytics Dashboard ████████████████████ 100%
Notifications & Monitoring ████████████████████ 100%
Email Notifications ████████████████████ 100%
Slack & Teams Integration ░░░░░░░░░░░░░░░░░░░░ 0%

What We Are Building

The email subsystem will support both immediate alerts and scheduled summaries.

Example email:

----------------------------------------------------------
Subject:
Tender deadline approaching: Azure Cloud Migration
----------------------------------------------------------
Hello Ben,
The submission deadline for:
Azure Cloud Migration Services
is in 2 days.
Match Score:
94%
Pipeline Stage:
Preparing Bid
Next Action:
Finalize pricing
Deadline:
August 15
Open Opportunity
----------------------------------------------------------

Daily digest:

----------------------------------------------------------
Today's Tender Summary
5 New Opportunities
3 High Match Opportunities
2 Upcoming Deadlines
1 Overdue Action
Pipeline Value:
€12.4M
Open Dashboard
----------------------------------------------------------

Why Email?

Users may not:

  • Open the application every day
  • Watch browser notifications
  • Leave the application running

But nearly every business user checks email throughout the day.

Email greatly increases the chance that important opportunities receive attention.


Notification Channels

Our architecture now supports multiple delivery channels.

Monitoring Engine
|
v
Notification Service
|
+------ In-App
+------ Email
+------ Slack (future)
+------ Teams (future)
+------ Mobile Push (future)

The monitoring engine creates notifications.

Delivery channels decide how users receive them.


Immediate vs Scheduled Delivery

Two delivery modes exist.

Immediate:

Deadline Today
Deadline Tomorrow
Critical Tender Update
High Match Opportunity

Scheduled:

Daily Digest
Weekly Summary
Pipeline Report

The delivery strategy depends on urgency.


Email Architecture

Notification
|
v
Email Queue
|
v
Email Worker
|
v
SMTP Provider
|
v
Recipient

Notice that the API never sends email directly.

Everything goes through the queue.


Why Use an Email Queue?

Sending email can take:

200 ms
500 ms
2 seconds

API requests should complete quickly.

Instead:

API
|
v
Queue Email
Return Response

The email worker performs delivery asynchronously.


Email Delivery Providers

The architecture should remain provider-independent.

Possible providers include:

SMTP
Amazon SES
Mailgun
Postmark
Resend
SendGrid

The application should communicate with an EmailService abstraction instead of a specific provider SDK.


Email Configuration

Add to .env:

EMAIL_PROVIDER=smtp
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=...
SMTP_PASSWORD=...
EMAIL_FROM=noreply@example.com
EMAIL_REPLY_TO=support@example.com

Never hardcode credentials.


Configuration Object

Create:

app/core/email_settings.py
from pydantic_settings import BaseSettings
class EmailSettings(BaseSettings):
provider: str = "smtp"
smtp_host: str
smtp_port: int = 587
smtp_username: str
smtp_password: str
email_from: str
reply_to: str

Email Queue Model

Create:

app/models/email_queue.py

Fields:

id
recipient
subject
html_body
text_body
status
retry_count
scheduled_at
sent_at
last_error
created_at

This queue decouples email generation from email delivery.


Delivery Status

Define:

class EmailStatus(str, Enum):
PENDING = "pending"
PROCESSING = "processing"
SENT = "sent"
FAILED = "failed"
CANCELLED = "cancelled"

The queue enables reliable retries.


SQLAlchemy Model

Example:

class EmailQueue(Base):
__tablename__ = "email_queue"
id = mapped_column(
Integer,
primary_key=True,
)
recipient = mapped_column(
String(255)
)
subject = mapped_column(
String(500)
)
html_body = mapped_column(
Text
)
text_body = mapped_column(
Text
)
status = mapped_column(
SqlEnum(
EmailStatus
),
default=EmailStatus.PENDING
)
retry_count = mapped_column(
Integer,
default=0
)
scheduled_at = mapped_column(
DateTime(timezone=True)
)
sent_at = mapped_column(
DateTime(timezone=True),
nullable=True
)
last_error = mapped_column(
Text,
nullable=True
)

Why Store Both HTML and Plain Text?

Some email clients prefer plain text.

Others display HTML.

Providing both versions improves compatibility and accessibility.


Email Template Structure

Create:

app/
├── email/
│ ├── templates/
│ │ ├── base.html
│ │ ├── deadline.html
│ │ ├── high_match.html
│ │ ├── digest.html
│ │ └── weekly_summary.html
│ │
│ ├── renderer.py
│ ├── service.py
│ └── worker.py

Separating templates keeps presentation independent from business logic.


Template Renderer

Create:

renderer.py

Example:

from jinja2 import (
Environment,
FileSystemLoader,
)

Render:

environment = Environment(
loader=FileSystemLoader(
"app/email/templates"
)
)
template = environment.get_template(
"deadline.html"
)
html = template.render(
title=...,
deadline=...,
score=...
)

Why Use Templates?

Bad:

body = f"""
Hello...
Deadline...
Score...
"""

Good:

Business Logic
Template Renderer
HTML

Templates are easier to maintain and redesign.


Email Service

Create:

email/service.py

Responsibilities:

  • Queue emails
  • Render templates
  • Validate recipients
  • Build email objects

Never send email directly.


Queue Email

Example:

def queue_email(
recipient,
subject,
template,
context,
):
...

Workflow:

Render Template
Insert Queue Record
Worker Sends Later

Email Worker

Create:

worker.py

Responsibilities:

Load Pending Emails
Send Email
Update Status
Retry Failures

The worker runs continuously or on a schedule.


SMTP Client

Example:

import smtplib

Create connection:

with smtplib.SMTP(
host,
port
) as smtp:

Authenticate:

smtp.login(
username,
password
)

Send:

smtp.send_message(
email
)

The SMTP implementation should be isolated behind an interface so providers can be replaced later.


Retry Strategy

Temporary failures should retry.

Example:

Attempt 1
1 minute
Attempt 2
5 minutes
Attempt 3
30 minutes
Give Up

Permanent failures:

Invalid email
Mailbox does not exist

should stop immediately.


Daily Digest Generator

Every morning:

Load User
Load Opportunities
Load Notifications
Generate Summary
Queue Email

The digest is personalized.


Daily Digest Sections

Include:

New Opportunities
High Match Opportunities
Upcoming Deadlines
Overdue Actions
Pipeline Summary
Open Dashboard Button

Users receive one concise overview.


Weekly Summary

Example metrics:

New Opportunities
Submitted Bids
Average Match Score
Pipeline Value
Industry Breakdown
Win Rate

Weekly summaries help management review performance.


Email Preferences

Users should control:

Immediate Alerts
Daily Digest
Weekly Summary
Marketing Emails
System Updates

Settings page:

Email Notifications
[x] Daily Digest
[x] Deadline Alerts
[x] High Match Alerts
[ ] Weekly Summary

Prevent Duplicate Emails

Use:

notification_id

inside the queue.

Never queue two identical emails for the same notification.


Delivery Tracking

Track:

Queued
Processing
Delivered
Failed
Cancelled

Administrators can diagnose delivery problems.


Logging

Record:

Queued Email
Sent Email
Failed Email
Retry Email
Cancelled Email

Email systems require observability.


Frontend Preferences

Add:

Email Preferences

Users can change:

Daily Digest
Weekly Summary
Immediate Alerts
Minimum Match Score

Testing

Backend tests:

Template Rendering
Queue Creation
SMTP Mocking
Retry Logic
Digest Generation

Frontend tests:

Preference Forms
Validation
Success Messages

End-to-End Test

New Tender
AI Analysis
High Match
Notification
Email Queue
SMTP
Inbox

This validates the complete communication pipeline.


Current Architecture

Tender Sources
|
v
AI Analysis
|
v
Matching
|
v
Notifications
|
+---- In-App
|
+---- Email
|
+---- Future Slack
|
+---- Future Teams

The platform now communicates proactively outside the application.


Current Deliverables

✔ Email queue
✔ Delivery tracking
✔ SMTP abstraction
✔ Template rendering
✔ Daily digests
✔ Weekly summaries
✔ Retry strategy
✔ Email preferences
✔ Delivery logging
✔ Worker architecture
✔ Testing strategy

Recommended Git Commit

git add .
git commit -m "Implement email notifications and daily digests"

Push:

git push origin main

What We Will Build Next

The platform now communicates through:

In-App Notifications
Email

Many organizations, however, work primarily in collaboration tools.

The next step is integrating with workplace communication platforms so users receive alerts without leaving their team’s workflow.

The next article in the development series is:

“Building Slack and Microsoft Teams Integrations for Real-Time Tender Collaboration.”


Conclusion

Email notifications and daily tender digests extend the AI Tender Opportunity Scanner beyond the browser by delivering timely, actionable information directly to users’ inboxes. By introducing an email queue, template rendering, delivery tracking, retry handling, and personalized digest generation, the platform gains a reliable communication layer that scales from individual consultants to enterprise procurement teams.

Combined with in-app notifications, monitoring, and analytics, users are now kept informed whether they are actively using the application or not. The next evolution is bringing those same alerts into collaborative environments such as Slack and Microsoft Teams, enabling faster team coordination and decision-making.

Discover more from BidRadar

Subscribe now to keep reading and get access to the full archive.

Continue reading