Notifications (SMS, Email, In-App Realtime)¶
The notification system is a standalone module — import it in any FastAPI route or service, no admin panel required.
It supports three channels:
- SMS — Twilio built-in, plus an extensible provider interface
- Email — SMTP built-in (stdlib
smtplib) - In-App Realtime — notifications stored in the DB and pushed to connected clients over WebSocket (with an SSE fallback)
Installation¶
The module itself has no hard third-party dependencies — Twilio is optional and imported lazily.
Quick start¶
from fastapi_admin_kit.notifications import (
NotificationService,
SMTPEmailProvider,
TwilioSMSProvider,
configure_notifications,
)
service = NotificationService()
# SMS — Twilio out of the box
service.register_sms_provider(
"twilio",
TwilioSMSProvider(
account_sid="AC...",
auth_token="...",
from_number="+15017122661",
),
)
# Email — SMTP out of the box
service.register_email_provider(
"smtp",
SMTPEmailProvider(
host="smtp.gmail.com",
port=587,
username="you@example.com",
password="app-password",
from_address="you@example.com",
),
)
# Mount the API endpoints
configure_notifications(app, service, prefix="/api/notifications")
Then send a notification:
await service.notify(
user_id="1234",
message="Your order has shipped!",
channels=["sms", "email"],
email="user@example.com",
phone="+15551234567",
)
notify() sends on all requested channels simultaneously and applies a
fallback to the configured fallback_channels when a channel fails.
Custom SMS providers¶
Extend SMSProvider and register it:
from fastapi_admin_kit.notifications import SMSProvider, SMSResult, SMSStatus
class MyCustomSMSProvider(SMSProvider):
name = "custom"
async def send(self, to: str, message: str) -> SMSResult:
# Call any SMS API (Vonage, AWS SNS, custom gateway, ...)
return SMSResult(message_id="msg-1", status=SMSStatus.QUEUED, to=to)
async def check_status(self, message_id: str) -> SMSStatus:
return SMSStatus.DELIVERED
service.register_sms_provider("custom", MyCustomSMSProvider())
service.set_default_sms_provider("custom")
A ready-to-copy example lives in
fastapi_admin_kit/notifications/sms/custom/example.py.
In-App realtime¶
Include the "in_app" channel to store the notification in the DB and push it
to connected clients instantly.
await service.notify(
user_id="1234",
message="Someone commented on your post.",
channels=["in_app"],
title="New comment",
data={"post_id": 42},
)
Clients connect via:
- WebSocket:
WS /api/notifications/ws?user_id=1234(or with a JWT?token=...) - SSE fallback:
GET /api/notifications/stream(authenticated)
The hub handles connection drops during publish and supports heartbeat /
stale-connection pruning. Fallback to polling: GET /api/notifications lists
in-app history; GET /api/notifications/unread-count returns the badge count.
Pushed messages are JSON with a type field: {"type": "notification", ...}
for new notifications and {"type": "read", "notification_id": ...} when a
notification is marked read (so all open tabs stay in sync).
Templates¶
Named templates render {placeholder} values from context:
from fastapi_admin_kit.notifications import NotificationTemplate, TemplateRegistry
registry = TemplateRegistry()
registry.register(
NotificationTemplate(
name="order_shipped",
title="Order {order_id} shipped",
body="Your order {order_id} is on the way.",
)
)
service.config.templates = registry
await service.notify(
user_id="1234",
message="", # body comes from the template
channels=["email"],
template="order_shipped",
context={"order_id": "ABC-123"},
email="user@example.com",
)
Preferences (opt-in / opt-out)¶
Per-user channel preferences are stored in the DB. Opting out blocks delivery:
await service.set_preference(user_id="1234", channel="sms", enabled=False, session=session)
prefs = await service.get_preferences(user_id="1234", session=session)
The API exposes PUT /notifications/preferences and
GET /notifications/preferences for the authenticated user.
Batch sending¶
await service.notify_many(
[
{"user_id": "1", "email": "a@example.com", "phone": "+15550000001"},
{"user_id": "2", "email": "b@example.com", "phone": "+15550000002"},
],
"System maintenance at midnight.",
channels=["email"],
)
API endpoints¶
| Method | Path | Description |
|---|---|---|
POST |
/notifications/send |
Send a notification |
POST |
/notifications/send/batch |
Batch send |
GET |
/notifications/ |
List in-app notifications (auth) |
GET |
/notifications/unread-count |
Unread badge count (auth) |
PUT |
/notifications/{id}/read |
Mark as read (auth) |
PUT |
/notifications/preferences |
Update channel preferences (auth) |
GET |
/notifications/preferences |
Read channel preferences (auth) |
WS |
/notifications/ws |
Realtime WebSocket stream |
GET |
/notifications/stream |
SSE fallback stream (auth) |
History / logs¶
Every per-channel delivery attempt is written to
admin_notification_logs. In-app notifications (title, body, channels, data,
read/unread) are persisted in admin_notifications, so the module doubles as
a notification history store.
Configuration¶
NotificationConfig controls defaults: