Notification Engine
The Notification Engine is CertusOrdo's multi-channel communication hub. It ensures the right people get the right alerts at the right time through the right channel — with compliance-grade audit trails for every message.
Overview
Decision Event → [NOTIFICATION ENGINE] → Delivery
│
├── Channel Selection (8 channels)
├── Rate Limiting
├── Quiet Hours Enforcement
├── Escalation Chains
└── Hash-Chained Audit Logs
Key Insight: Most alert systems over-notify. CertusOrdo's autonomous decision-making means you only hear from us when human judgment is genuinely required.
When Notifications Fire
| Event | Autonomous? | Notification |
|---|---|---|
COMMIT |
✅ Yes | None (dashboard only) |
COMMIT_WITH_WARNING |
✅ Yes | Dashboard + optional email summary |
ROLLBACK_AND_RETRY (success) |
✅ Yes | None |
ROLLBACK_AND_RETRY (max retries) |
✅ Yes | |
HOLD_FOR_REVIEW |
❌ Human needed | Email + SMS |
ROLLBACK_AND_TERMINATE |
✅ Yes | Email + SMS |
ESCALATE |
❌ Human needed | Email + SMS + escalation chain |
| Security threat detected | ✅ Auto-terminate | Email + SMS + Voice Call |
Target: 95% of transactions complete without any notification.
Supported Channels
| Channel | Use Case | Latency |
|---|---|---|
| Dashboard | Real-time monitoring | < 100ms |
| Standard alerts, summaries | < 30s | |
| SMS | Urgent alerts | < 10s |
| Voice Call | Critical escalations | < 60s |
| Webhook | System integrations | < 5s |
| Slack | Team notifications | < 5s |
| Microsoft Teams | Enterprise teams | < 5s |
| PagerDuty | On-call rotations | < 5s |
Notification Preferences
Configure per-organization notification behavior:
notification_preferences = {
"org_id": "uuid",
# Channel configuration
"channels": {
"email": {
"enabled": True,
"recipients": ["alerts@company.com", "security@company.com"],
"min_priority": "medium"
},
"sms": {
"enabled": True,
"recipients": ["+1234567890"],
"min_priority": "high"
},
"voice": {
"enabled": True,
"recipients": ["+1234567890"],
"min_priority": "critical"
},
"slack": {
"enabled": True,
"webhook_url": "https://hooks.slack.com/...",
"channel": "#agent-alerts",
"min_priority": "low"
},
"pagerduty": {
"enabled": True,
"routing_key": "pd_routing_key",
"min_priority": "high"
}
},
# Routing rules
"routing": {
"security_threats": ["email", "sms", "voice", "pagerduty"],
"human_review": ["email", "sms", "slack"],
"session_terminated": ["email", "slack"],
"daily_summary": ["email"]
},
# Preferences
"timezone": "America/New_York",
"language": "en",
"digest_enabled": True,
"digest_schedule": "0 9 * * *" # 9 AM daily
}
Quiet Hours
Prevent non-critical notifications during off-hours:
quiet_hours_schedule = {
"org_id": "uuid",
"enabled": True,
"schedules": {
"monday": {"start": "22:00", "end": "07:00"},
"tuesday": {"start": "22:00", "end": "07:00"},
"wednesday": {"start": "22:00", "end": "07:00"},
"thursday": {"start": "22:00", "end": "07:00"},
"friday": {"start": "22:00", "end": "07:00"},
"saturday": {"start": "00:00", "end": "23:59"}, # All day
"sunday": {"start": "00:00", "end": "23:59"} # All day
},
# What happens during quiet hours
"behavior": {
"critical": "deliver", # Always deliver
"high": "deliver", # Always deliver
"medium": "queue", # Hold until quiet hours end
"low": "suppress" # Don't deliver at all
},
# Override patterns
"overrides": [
{
"date": "2026-01-20", # MLK Day
"all_day_quiet": True
}
]
}
Escalation Chains
Define multi-level escalation paths:
escalation_chain = {
"name": "security_incident",
"description": "Critical security escalation path",
"steps": [
{
"level": 1,
"name": "Security Team",
"contacts": ["security@company.com", "+1234567890"],
"channels": ["email", "sms"],
"timeout_minutes": 15,
"action_on_timeout": "escalate"
},
{
"level": 2,
"name": "Security Lead",
"contacts": ["security-lead@company.com", "+1234567891"],
"channels": ["email", "sms", "voice"],
"timeout_minutes": 15,
"action_on_timeout": "escalate"
},
{
"level": 3,
"name": "CISO",
"contacts": ["ciso@company.com", "+1234567892"],
"channels": ["email", "sms", "voice"],
"timeout_minutes": 30,
"action_on_timeout": "auto_rollback"
}
],
# Acknowledgment stops escalation
"ack_required": True,
"ack_methods": ["email_reply", "sms_reply", "dashboard_button"]
}
Escalation Flow:
Alert Generated
│
↓
Level 1: Security Team (15 min)
│
├── ACK received → Stop, execute decision
│
↓ (no response)
Level 2: Security Lead (15 min)
│
├── ACK received → Stop, execute decision
│
↓ (no response)
Level 3: CISO (30 min)
│
├── ACK received → Stop, execute decision
│
↓ (no response)
Auto-rollback + Incident Report
Rate Limiting
Prevent notification fatigue:
rate_limits = {
"org_id": "uuid",
"limits": {
"email": {
"per_recipient_per_hour": 10,
"per_recipient_per_day": 50,
"org_wide_per_hour": 100
},
"sms": {
"per_recipient_per_hour": 5,
"per_recipient_per_day": 20,
"org_wide_per_hour": 50
},
"voice": {
"per_recipient_per_hour": 2,
"per_recipient_per_day": 5,
"org_wide_per_hour": 10
}
},
# What happens when limit hit
"on_limit_reached": {
"action": "queue_with_digest",
"digest_interval_minutes": 60
},
# Exceptions
"bypass_for_priority": ["critical"] # Critical always goes through
}
Notification Templates
Customize message content with Jinja2 templates:
notification_template = {
"name": "human_review_required",
"channel": "email",
"subject": "[CertusOrdo] Action Required: {{ decision_type }} for {{ agent_name }}",
"body": """
<h2>Human Review Required</h2>
<p>Agent <strong>{{ agent_name }}</strong> requires your review.</p>
<h3>Transaction Details</h3>
<ul>
<li>Transaction ID: {{ transaction_id }}</li>
<li>Action: {{ action_type }}</li>
<li>Value: ${{ value_usd | number_format }}</li>
<li>Confidence: {{ confidence_score | percent }}</li>
</ul>
<h3>Anomalies Detected</h3>
<ul>
{% for anomaly in anomalies %}
<li>{{ anomaly.message }} ({{ anomaly.severity }})</li>
{% endfor %}
</ul>
<h3>Recommended Action</h3>
<p>{{ recommended_action }}</p>
<p>
<a href="{{ approve_url }}">✅ Approve</a> |
<a href="{{ reject_url }}">❌ Reject</a> |
<a href="{{ dashboard_url }}">View in Dashboard</a>
</p>
<p><em>This request will auto-{{ timeout_action }} in {{ timeout_minutes }} minutes if no response.</em></p>
""",
"variables": [
"decision_type", "agent_name", "transaction_id", "action_type",
"value_usd", "confidence_score", "anomalies", "recommended_action",
"approve_url", "reject_url", "dashboard_url", "timeout_action", "timeout_minutes"
]
}
API Reference
Send Notification
Request Body:
{
"org_id": "uuid",
"event_type": "human_review_required",
"priority": "high",
"channels": ["email", "sms"],
"context": {
"decision_id": "uuid",
"transaction_id": "uuid",
"agent_name": "Financial Agent",
"action_type": "wire_transfer",
"value_usd": 25000.00,
"confidence_score": 0.65,
"anomalies": [
{
"message": "Transaction exceeds daily limit",
"severity": "medium"
}
]
},
"escalation_chain": "financial_approvals",
"timeout_minutes": 30
}
Response:
{
"notification_id": "uuid",
"status": "queued",
"channels_targeted": ["email", "sms"],
"recipients": [
{
"channel": "email",
"address": "finance@company.com",
"status": "queued"
},
{
"channel": "sms",
"address": "+1234567890",
"status": "queued"
}
],
"escalation_chain_active": true,
"timeout_at": "2026-01-23T21:30:00Z"
}
Acknowledge Notification
Request Body:
{
"acknowledged_by": "user@company.com",
"action_taken": "approved",
"notes": "Verified with customer"
}
Stops escalation chain when acknowledged.
Get Notification Status
Get Notification Metrics
Returns delivery rates, latency percentiles, channel breakdown.
Database Schema
The Notification Engine uses 11 tables for enterprise-grade reliability:
| Table | Purpose |
|---|---|
notification_preferences |
Per-org channel config, routing rules |
notification_logs |
Hash-chained audit trail |
notification_queue |
Transient queue with retry, idempotency |
notification_dead_letter_queue |
Permanent failures for review |
escalation_chains |
Escalation path definitions |
escalation_steps |
Individual step configuration |
notification_templates |
Jinja2 templates per org/channel |
notification_rate_limits |
Per-channel/recipient limits |
quiet_hours_schedules |
Day-specific quiet hours |
notification_metrics |
Delivery metrics, latency percentiles |
notification_acknowledgments |
ACK tracking, stops escalation |
Hash-Chained Audit Logs
For compliance (SOC 2, SEC 17a-4), every notification is logged with cryptographic integrity:
{
"log_id": "uuid",
"notification_id": "uuid",
"timestamp": "2026-01-23T20:00:00Z",
"event": {
"type": "notification_sent",
"channel": "email",
"recipient": "finance@company.com",
"template": "human_review_required",
"priority": "high"
},
"outcome": {
"status": "delivered",
"provider_response": "250 OK",
"latency_ms": 1250
},
"integrity": {
"hash": "sha256:abc123...",
"previous_hash": "sha256:xyz789...",
"chain_position": 15420
}
}
Hash Chain Properties: - Each log entry includes hash of previous entry - Tampering with any record breaks the chain - Supports 7-year retention for SEC 17a-4 - Export to WORM storage for compliance audits
Delivery Guarantees
| Guarantee | Implementation |
|---|---|
| At-least-once | Idempotency keys prevent duplicates |
| Ordered | Queue ensures sequence within escalation |
| Durable | PostgreSQL persistence, no message loss |
| Retriable | Exponential backoff with dead letter queue |
Retry Policy:
Attempt 1: Immediate
Attempt 2: 30 seconds
Attempt 3: 2 minutes
Attempt 4: 10 minutes
Attempt 5: 1 hour
→ Dead Letter Queue (manual review)
Channel-Specific Notes
Email (SendGrid/SES)
- Supports HTML and plain text
- Tracks opens and clicks
- SPF/DKIM/DMARC authentication
- Bounce handling
SMS (Twilio)
- Supports international numbers
- Character limit handling (splits long messages)
- Delivery receipts
- Opt-out compliance
Voice (Twilio/Vapi)
- Text-to-speech for dynamic content
- DTMF input for acknowledgment (press 1 to approve)
- Voicemail detection
- Call recording (optional, for compliance)
Slack
- Block Kit formatting for rich messages
- Interactive buttons for approve/reject
- Thread replies for updates
- @mentions support
PagerDuty
- Events API v2 integration
- Severity mapping to PD urgency
- Incident deduplication
- Auto-resolve on acknowledgment
Integration Flow
async def notify_on_decision(decision):
# Only notify for human-required decisions
if decision.decision in ["COMMIT", "COMMIT_WITH_WARNING"]:
return # No notification needed
# Determine channels based on priority
channels = select_channels(
decision.priority,
decision.org_preferences
)
# Check quiet hours
if in_quiet_hours(decision.org_id) and decision.priority != "critical":
queue_for_later(decision)
return
# Send notifications
notification = await notification_engine.send(
org_id=decision.org_id,
event_type=decision.decision,
priority=decision.priority,
channels=channels,
context=build_context(decision),
escalation_chain=get_escalation_chain(decision),
timeout_minutes=decision.timeout_minutes
)
# Track in audit log
await audit_log.record(
event="notification_sent",
notification_id=notification.id,
decision_id=decision.id
)
return notification
Metrics Dashboard
Track notification system health:
| Metric | Target | Alert Threshold |
|---|---|---|
| Delivery rate | > 99.5% | < 98% |
| Email latency (P95) | < 30s | > 60s |
| SMS latency (P95) | < 10s | > 30s |
| Escalation completion | > 95% | < 90% |
| Acknowledgment rate | > 80% | < 60% |
Next Steps
The Learning Engine (coming soon) will use notification outcomes to optimize:
- Which channels work best for which users
- Optimal escalation timing
- Content that drives faster acknowledgment
- Quiet hours that minimize disruption while ensuring coverage