Skip to content
GitHub

Notification (lexigram-notification)

SMS, push, and email notification delivery with Named DI multi-backend support for the Lexigram Framework.


lexigram-notification provides a unified notification delivery system with SMS (Twilio), push (FCM, APNS), email (SMTP, SendGrid), and per-user inbox storage. The package is organized into three subpackages: root (SMS/push), mailer (email), and inbox (in-app notification storage). Root and mailer each wire their own module; the inbox is wired by InboxProvider (not a module).


Full documentation: docs.lexigram.dev

Terminal window
uv add lexigram-notification
# With SendGrid email
uv add "lexigram-notification[sendgrid]"
# With Twilio SMS
uv add "lexigram-notification[twilio]"
# With APNS push
uv add "lexigram-notification[apns]"
from lexigram.di.module import Module, module
from lexigram.notification import NotificationModule
from lexigram.notification.config import (
FCMDriverConfig,
MailerConfig,
NamedMailerConfig,
NamedPushConfig,
NamedSMSConfig,
NotificationConfig,
SMTPDriverConfig,
TwilioDriverConfig,
)
from lexigram.notification.mailer import MailerModule
@module(
imports=[
NotificationModule.configure(
NotificationConfig(
sms_backends=[
NamedSMSConfig(
name="alerts",
primary=True,
driver="twilio",
twilio=TwilioDriverConfig(
account_sid="AC...",
auth_token="secret",
from_number="+15550000000",
),
)
],
push_backends=[
NamedPushConfig(
name="mobile",
primary=True,
driver="fcm",
fcm=FCMDriverConfig(server_key="fcm-key"),
)
],
)
),
MailerModule.configure(
MailerConfig(
backends=[
NamedMailerConfig(
name="transactional",
primary=True,
driver="smtp",
from_email="noreply@example.com",
smtp=SMTPDriverConfig(host="smtp.example.com", port=587),
)
]
)
),
]
)
class AppModule(Module):
pass

Zero-config usage: Call any .configure() with no arguments to use all defaults.

application.yaml
notification:
sms_backends: []
push_backends: []
mailer:
backends:
- name: transactional
primary: true
driver: smtp
from_email: "noreply@example.com"
smtp:
host: "smtp.example.com"
port: 587
inbox:
store_backend: "database"
retention_days: 30
Section titled “Option 2 — Profiles + Environment Variables (recommended)”
Terminal window
export LEX_NOTIFICATION__INBOX__STORE_BACKEND=database
from lexigram.notification import NotificationModule
from lexigram.notification.config import (
MailerConfig,
NamedMailerConfig,
NotificationConfig,
SMTPDriverConfig,
)
from lexigram.notification.mailer import MailerModule
NotificationModule.configure(NotificationConfig())
MailerModule.configure(
MailerConfig(
backends=[
NamedMailerConfig(
name="transactional",
primary=True,
driver="smtp",
from_email="noreply@example.com",
smtp=SMTPDriverConfig(host="smtp.example.com", port=587),
)
]
)
)
FieldDefaultEnv varDescription
notification.sms_backends[]LEX_NOTIFICATION__SMS_BACKENDSNamed SMS backend configs
notification.push_backends[]LEX_NOTIFICATION__PUSH_BACKENDSNamed push backend configs
mailer.backends[n].driverLEX_NOTIFICATION__MAILER__BACKENDS__N__DRIVERMailer driver: smtp, sendgrid
mailer.backends[n].from_emailLEX_NOTIFICATION__MAILER__BACKENDS__N__FROM_EMAILSender email address
inbox.store_backend"database"LEX_NOTIFICATION__INBOX__STORE_BACKENDInbox store: database or memory
inbox.retention_days30LEX_NOTIFICATION__INBOX__RETENTION_DAYSDays to retain inbox messages
inbox.max_page_size50LEX_NOTIFICATION__INBOX__MAX_PAGE_SIZEMax messages returned per page
MethodDescription
NotificationModule.configure(config)Register SMS and push backends; exports SMSChannelProtocol, PushChannelProtocol
NotificationModule.stub()Empty config — no backends configured
MailerModule.configure(config)Register named mailer backends; exports MailerProtocol
MailerModule.stub(config=None)Empty or caller-supplied config for tests

Inbox support ships as a service (InboxService) wired by InboxProvider (in lexigram.notification.di), not by NotificationModule — include InboxProvider in your module’s providers list when you need the inbox.

When running under lexigram-admin, the package registers a notification contributor (entry point lexigram.admin.contributors) that exposes:

EndpointDescription
GET /admin/notifications/inboxCurrent user’s persisted inbox as JSON (unread_count + notifications, used by the topbar bell)
POST /admin/notifications/read/{message_id}Mark one message read
POST /admin/notifications/read-allMark all of the user’s messages read
GET /admin/notificationsInbox management page inside the admin shell
notifications.inboxHealth check (admin/health fragments)

Real-time updates: InboxService.send() fires the notification.inbox.sent action hook (constant INBOX_SENT_HOOK in lexigram-contracts); the admin realtime sub-provider forwards it to the SSE hub so open bells update live.

  • SMS delivery — Twilio backend via TwilioSMS
  • Push delivery — FCM and APNS backends with send_batch() support
  • Email delivery — SMTP (blocking, runs in executor) and SendGrid REST API
  • Retrying mailer — wraps any MailerProtocol with exponential backoff and delivery-store tracking
  • Per-user inbox — SQL or in-memory backend with InboxService (send, get_inbox, mark_read, delete, count_unread)
  • Multi-backend — SMS and push backends registered by name from NotificationConfig.sms_backends / push_backends; the primary backend also receives the unnamed bindings
async with Application.boot(
modules=[NotificationModule.stub(), MailerModule.stub()]
) as app:
# your test code
...
FileWhat it contains
src/lexigram/notification/module.pyNotificationModule.configure(), .stub()
src/lexigram/notification/config.pyNotificationConfig, NamedSMSConfig, NamedPushConfig, MailerConfig, NamedMailerConfig, SMTPDriverConfig, InboxConfig
src/lexigram/notification/di/provider.pyNotificationProvider
src/lexigram/notification/di/inbox_provider.pyInboxProvider — wires InboxStoreProtocol + InboxService
src/lexigram/notification/mailer/module.pyMailerModule.configure(), .stub()
src/lexigram/notification/mailer/smtp_mailer.pySMTP mailer backend (blocking, executor-run)
src/lexigram/notification/mailer/sendgrid_mailer.pySendGrid REST API mailer backend
src/lexigram/notification/mailer/retrying_mailer.pyRetryingMailer — exponential backoff + delivery tracking
src/lexigram/notification/mailer/mailable.pyMailable — message builder
src/lexigram/notification/inbox/service.pyInboxService — send, get_inbox, mark_read, delete, count_unread
src/lexigram/notification/inbox/memory.pyInMemoryInboxStore
src/lexigram/notification/inbox/database.pyDatabaseInboxStore