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:
- 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 Servicesis in 2 days.Match Score:94%Pipeline Stage:Preparing BidNext Action:Finalize pricingDeadline:August 15Open Opportunity----------------------------------------------------------
Daily digest:
----------------------------------------------------------Today's Tender Summary5 New Opportunities3 High Match Opportunities2 Upcoming Deadlines1 Overdue ActionPipeline Value:€12.4MOpen 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 | vNotification 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 TodayDeadline TomorrowCritical Tender UpdateHigh Match Opportunity
Scheduled:
Daily DigestWeekly SummaryPipeline Report
The delivery strategy depends on urgency.
Email Architecture
Notification | vEmail Queue | vEmail Worker | vSMTP Provider | vRecipient
Notice that the API never sends email directly.
Everything goes through the queue.
Why Use an Email Queue?
Sending email can take:
200 ms500 ms2 seconds
API requests should complete quickly.
Instead:
API | vQueue EmailReturn Response
The email worker performs delivery asynchronously.
Email Delivery Providers
The architecture should remain provider-independent.
Possible providers include:
SMTPAmazon SESMailgunPostmarkResendSendGrid
The application should communicate with an EmailService abstraction instead of a specific provider SDK.
Email Configuration
Add to .env:
EMAIL_PROVIDER=smtpSMTP_HOST=smtp.example.comSMTP_PORT=587SMTP_USERNAME=...SMTP_PASSWORD=...EMAIL_FROM=noreply@example.comEMAIL_REPLY_TO=support@example.com
Never hardcode credentials.
Configuration Object
Create:
app/core/email_settings.py
from pydantic_settings import BaseSettingsclass 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:
idrecipientsubjecthtml_bodytext_bodystatusretry_countscheduled_atsent_atlast_errorcreated_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 EmailsSend EmailUpdate StatusRetry 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 emailMailbox 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 OpportunitiesHigh Match OpportunitiesUpcoming DeadlinesOverdue ActionsPipeline SummaryOpen Dashboard Button
Users receive one concise overview.
Weekly Summary
Example metrics:
New OpportunitiesSubmitted BidsAverage Match ScorePipeline ValueIndustry BreakdownWin Rate
Weekly summaries help management review performance.
Email Preferences
Users should control:
Immediate AlertsDaily DigestWeekly SummaryMarketing EmailsSystem 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:
QueuedProcessingDeliveredFailedCancelled
Administrators can diagnose delivery problems.
Logging
Record:
Queued EmailSent EmailFailed EmailRetry EmailCancelled Email
Email systems require observability.
Frontend Preferences
Add:
Email Preferences
Users can change:
Daily DigestWeekly SummaryImmediate AlertsMinimum Match Score
Testing
Backend tests:
Template RenderingQueue CreationSMTP MockingRetry LogicDigest Generation
Frontend tests:
Preference FormsValidationSuccess 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 | vAI Analysis | vMatching | vNotifications | +---- 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 NotificationsEmail
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.