Skip to main content

Notification Service — Overview

The Notify Service is the single notification delivery gateway for all modules. Modules never send emails, SMS messages, or Telegram messages directly — they call kernel.notify().send() and the service handles delivery, retries, history, and rate limiting.

The service is built around a pluggable channel architecture. Two channels are built in for every deployment. All other channels (email, sms, telegram, discord, slack, webhook, etc.) are installed as adapters registered via the module manifest.


Technical Stack

ComponentTechnologyRole
Go runtimenhooyr.io/websocket + goroutinesWebSocket server (thousands of concurrent connections)
Message queueRabbitMQ (rabbitmq/amqp091-go)Async delivery queue + retry
Replay bufferValkey (AOF persistence)WebSocket reconnect replay — 100 messages, 1 h TTL
StoragePostgreSQLNotification history (90 days), failed log (180 days)

Built-in Channels

Two channels are always present — no adapter installation required:

ChannelTransportUse case
WebSocketwss://notify.platform.io/wsReal-time in-browser notifications, live dashboard updates
Browser PushService Worker + FCM / APNsPush notifications when the browser tab is not active

Pluggable Channels (Adapters)

All other channels are delivered via registered adapters. An adapter is a module that implements the NotificationChannel interface and declares itself in module.manifest.json under notificationChannels. When installed, the channel automatically appears in Settings → Notifications.

AdapterTypical use case
Email (SMTP / SendGrid / Mailgun)Transactional emails, newsletters
SMS (Twilio / Vonage / any gateway)OTP codes, alerts
TelegramBot notifications
DiscordDev alerts to a channel
SlackTeam notifications
WhatsApp / Viber / any messengerRegional preference
WebhookPOST to any external URL

The platform does not bundle specific SMTP or SMS providers. A module installs the adapter for whichever provider the tenant prefers.


Async Delivery Model

Every send() call returns 202 Accepted immediately. Delivery is asynchronous via RabbitMQ. This means the caller is never blocked by a slow external provider (SendGrid latency, Telegram rate limit, etc.).

Module code: kernel.notify().send(…)

│ POST /api/v1/notifications → 202 Accepted

Notify Service enqueues to RabbitMQ
Returns { notificationId }

│ Worker dequeues and calls adapter

Adapter delivers (email/telegram/…)
Status stored in PostgreSQL


Module polls GET /api/v1/notifications/:id
or receives notify.notification.sent event

If delivery fails, the service retries automatically using exponential backoff (up to 5 attempts). After exhausting retries the notification is written to the failed table and made visible in Admin → Notifications → Failed.


Tenant Isolation

Every tenant has an isolated RabbitMQ queue: notify.outgoing.{tenantId}. One tenant cannot block or delay notifications for another tenant — queue exhaustion only affects the tenant whose queue is full.

Queue policy per tenant queue:

ParameterValue
Max length10 000 messages (x-max-length: 10000)
Overflow policyx-overflow: reject-publish
On overflow503 Service Unavailable for new batch calls
Env overrideNOTIFY_RABBITMQ_MAX_QUEUE_LENGTH=10000

REST API at a Glance

MethodEndpointDescription
POST/api/v1/notificationsSend notification
POST/api/v1/notifications/batchSend to multiple recipients
GET/api/v1/notifications/:idNotification status
GET/api/v1/notificationsNotification history (paginated)
POST/api/v1/notifications/:id/retryManual retry of failed notification
GET/api/v1/notifications/failedFailed notifications (paginated)
POST/api/v1/notifications/templatesCreate template
GET/api/v1/notifications/templatesList templates
POST/api/v1/notifications/channelsRegister channel adapter
GET/api/v1/notifications/channelsList registered channels

History Retention

StoreRetentionPurpose
PostgreSQL notification history90 days (NOTIFY_HISTORY_RETENTION_DAYS)Standard delivery log
PostgreSQL notifications_failed180 daysIncident investigation
Audit Service7 yearsEvery send recorded as audit event

  • Sending Notificationssend(), sendBatch(), priorities
  • Channels — pluggable channel interface, registration
  • TemplatessendFromTemplate(), variable substitution (Batch 15)
  • WebSocket Protocol — heartbeat, reconnect, replay buffer (Batch 15)
  • Retry & DLQ — backoff schedule, manual retry, fallback policy (Batch 15)
  • Rate Limiting — per-tenant / per-module limits (Batch 15)