Skip to content

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 Email
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
Email 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

POST /v1/notifications/send
Content-Type: application/json
X-API-Key: your_api_key

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

POST /v1/notifications/{notification_id}/ack
Content-Type: application/json
X-API-Key: your_api_key

Request Body:

{
  "acknowledged_by": "user@company.com",
  "action_taken": "approved",
  "notes": "Verified with customer"
}

Stops escalation chain when acknowledged.

Get Notification Status

GET /v1/notifications/{notification_id}
X-API-Key: your_api_key

Get Notification Metrics

GET /v1/notifications/metrics?org_id={uuid}&period=7d
X-API-Key: your_api_key

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