The notification system has two backend-only layers:
- existing user notification outbox for in-app operational messages
- notification delivery engine for sanitized backend intelligence events
Neither layer is a trading alert engine, broker workflow, order system, auto-trading path, or financial-advice channel.
Delivery channels are stored in notification_channels.
Supported channel types:
webhook
email
telegram
discordSupported channel statuses:
active
paused
archivedChannel config is provider-specific and must not contain inline secrets. Use secret_ref for
future secret resolution. Active channels can filter event types, severity, and quiet hours.
Delivery events are stored in notification_events.
Supported event types:
signal.classified
signal.review_recommended
outcome.evaluated
digest.created
data_quality.degraded
market_memory.stale
reasoning.action_due
readiness.blocked
operator_review.opened
scan.completed
provider_health.degraded
gap_recovery.neededdigest.created events are not created or delivered automatically by digest generation. They must
be created explicitly through the notification event API and delivered explicitly through the
delivery API.
Supported severities:
info
low
medium
high
criticalSupported event statuses:
pending
held
delivered
partially_delivered
blocked
cancelled
failedInbox statuses are separate from delivery status:
unread
read
acknowledged
archivedBefore an event is persisted for delivery, the service sanitizes its title, summary, and payload.
The safety layer:
- blocks direct trading instruction and financial-advice language
- redacts secrets, credentials, tokens, and authorization fields
- removes raw candle series and raw chart image payloads
- removes unsafe LLM output fields
- truncates payloads to
NOTIFICATION_MAX_PAYLOAD_BYTES - stores safety status and redaction metadata in the event payload
Deliverable payloads must not contain broker instructions, order instructions, raw candles, raw images, secrets, unsafe LLM output, or trade-advice language.
The default dedupe key is built from:
workspace_id + event_type + source_type + source_id + severityIf an event with the same key was delivered, partially delivered, or held inside
NOTIFICATION_DEDUPE_WINDOW_SECONDS, creating a duplicate returns the existing event.
Channels can define quiet hours with:
{
"enabled": true,
"timezone": "UTC",
"start": "22:00",
"end": "07:00",
"behavior": "hold"
}behavior can be hold or skip. Quiet hours are evaluated at delivery time. This phase does not
schedule a later external delivery worker for held events.
webhook performs a real HTTP POST when:
NOTIFICATIONS_ENABLED=true- the event is explicitly delivered
- the channel is active
- the channel matches event type and severity filters
- quiet hours allow delivery
config_json.target_urlorconfig_json.urlis configured
email, telegram, and discord are production-shaped safe stubs. They validate expected
configuration shape and record skipped attempts until secret resolution and provider-specific
sending are implemented.
No adapter sends messages during startup or tests.
Channel APIs:
POST /notification-channels
GET /notification-channels
GET /notification-channels/{channel_id}
PATCH /notification-channels/{channel_id}
POST /notification-channels/{channel_id}/archiveEvent APIs:
POST /notification-events
GET /notification-events
GET /notification-events/{event_id}
POST /notification-events/{event_id}/read
POST /notification-events/{event_id}/acknowledge
POST /notification-events/{event_id}/archive
POST /notification-events/{event_id}/deliver
GET /notification-events/{event_id}/attemptsThe inbox APIs update in-product review state only. They do not deliver events externally and do not alter source artifacts.
Existing in-app notification APIs remain available:
PUT /notifications/preferences
GET /notifications/preferences
POST /notifications
GET /notifications
GET /notifications/{notification_id}
POST /notifications/{notification_id}/read
POST /notifications/dispatch-due
GET /notifications/worker/statusNOTIFICATIONS_ENABLED=false
NOTIFICATION_DELIVERY_TIMEOUT_SECONDS=10
NOTIFICATION_MAX_PAYLOAD_BYTES=16000
NOTIFICATION_DEDUPE_WINDOW_SECONDS=3600
NOTIFICATION_DEFAULT_QUIET_HOURS_TIMEZONE=UTC
NOTIFICATION_WEBHOOK_USER_AGENT=trading-intelligence-notifications/0.1Existing worker settings still control the legacy in-app notification worker.
The web /notifications page is an in-app inbox over sanitized notification_events. It supports
filtering, event review, source links, delivery-attempt inspection, and read/acknowledge/archive
state when the backend inbox APIs are available. It does not trigger external delivery by default.
The notification engine does not:
- execute broker actions
- place orders
- auto-trade
- send market advice
- include raw candles or raw chart images
- include secrets or unsafe LLM output
- send external messages unless configured and explicitly invoked