Root cause: Microsoft Teams Incoming Webhooks require MessageCard JSON
format. The service was sending generic Aegis JSON which Teams rejected
with a 400, incrementing failure_count on every dispatch.
Fix: _send_webhook() now auto-detects the target from the URL:
- webhook.office.com / teams.microsoft.com → Teams MessageCard
(colored card with event title + key/value facts table)
- hooks.slack.com → Slack attachments format
- everything else → current generic Aegis JSON
Also resets failure_count=0 in production so the webhook starts fresh.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add dispatch_webhook_targeted() to webhook_service for rule-specific delivery
- evaluate_all_rules() now dispatches in-app notifications (admins/leads) and
webhooks after each alert fires (targeted + global alert.fired broadcast)
- APScheduler: _run_alert_evaluation() job registered hourly alongside existing jobs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Phase 6.1: WebhookConfig model, CRUD router (/api/v1/webhooks, admin-only),
dispatch_webhook() with HMAC signing; integrated into test validation,
campaign completion, and MITRE sync job
- Phase 7.1: SMTP email service with send_test_validated_email,
send_campaign_completed_email, send_new_mitre_techniques_email;
notify_role_with_email() added to notification_service
- Phase 7.2: notification_preferences and jira_account_id on User model;
PATCH /users/me/preferences endpoint; Alembic migrations b031phase6 and b032phase7
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>