Novu: Open Source Notification Infrastructure for Devs
Imagine building a SaaS product where every user action triggers an email, an in‑app banner, a push notification, or even a Slack message. Managing those channels, handling retries, and keeping the codebase clean quickly becomes a nightmare. That’s where Novu steps in – an open‑source notification infrastructure that abstracts the plumbing so developers can focus on the business logic.
Why Novu Matters for Modern Apps
Most teams start with a simple SMTP integration or a third‑party service like SendGrid. As the product scales, the notification matrix expands: mobile push, SMS, webhooks, and team collaboration tools. Keeping track of API keys, payload formats, and delivery guarantees across these providers leads to duplicated code and fragile error handling.
Novu solves this by offering a unified API, a configurable workflow engine, and a plug‑in architecture that supports any channel you can imagine. Because it’s open source, you can self‑host, extend, or contribute back – a rare combination of flexibility and community support.
Core Concepts at a Glance
Providers
Providers are the actual services that send the notification – think SendGrid for email, Firebase Cloud Messaging for push, or Twilio for SMS. Novu ships with dozens of built‑in providers and lets you add custom ones via a simple interface.
Templates
Templates define the content and layout of a notification. They can be plain text, HTML, or markdown, and they support dynamic variables like {{ user.name }}. Templates are stored centrally, enabling non‑technical team members to edit copy without touching code.
Workflows
A workflow is a series of steps that determine how and when a notification is sent. You can chain multiple providers, add conditional branching, and set retry policies – all without writing a single line of procedural code.
Subscribers
Subscribers represent the end users or entities that receive notifications. Each subscriber can have multiple “identifiers” (email, phone number, device token) linked to the appropriate provider.
Getting Started: A Minimal Setup
First, install the Novu SDK for Python. It’s lightweight and works seamlessly with FastAPI, Django, or any async framework.
pip install novu-sdk
Next, initialize the client with your API key. If you’re self‑hosting, point the base_url to your own Novu instance.
from novu import Novu
# Replace with your actual API key
novu = Novu(api_key="YOUR_NOVU_API_KEY")
Now, let’s create a simple email template directly from code. In production you’d manage this via the Novu dashboard, but the SDK makes automation easy.
template = novu.templates.create(
name="welcome-email",
subject="Welcome to Codeyaan, {{ user.name }}!",
content="""
Hello {{ user.name }},
Thanks for joining Codeyaan. We're thrilled to have you!
Happy coding,
The Codeyaan Team
""",
channel="email",
is_active=True,
)
print(f"Created template ID: {template.id}")
Finally, trigger the notification for a new subscriber.
subscriber = novu.subscribers.create(
subscriber_id="user_12345",
email="alice@example.com"
)
novu.notifications.trigger(
subscriber_id=subscriber.id,
template_id=template.id,
payload={"user": {"name": "Alice"}}
)
This three‑step flow—initialize, define a template, trigger—covers 80% of typical use cases. The heavy lifting—delivery, retries, and provider selection—is handled by Novu.
Real‑World Use Cases
1. Multi‑Channel Order Confirmation
E‑commerce platforms need to confirm orders via email, SMS, and in‑app alerts. With Novu, you define a single workflow that branches based on user preferences.
- Check if the user opted in for SMS.
- If yes, send an SMS via Twilio.
- Always send an email via SendGrid.
- Push an in‑app notification using Firebase.
Here’s a concise Python snippet that sets up such a workflow using the SDK’s create_workflow method.
workflow = novu.workflows.create(
name="order-confirmation",
steps=[
{
"type": "conditional",
"condition": "{{ subscriber.preferences.sms }} == true",
"true_steps": [
{"type": "notification", "template_id": "sms-order-template"}
],
"false_steps": []
},
{"type": "notification", "template_id": "email-order-template"},
{"type": "notification", "template_id": "push-order-template"},
],
active=True,
)
print(f"Workflow ID: {workflow.id}")
Pro tip: Store user preferences in a fast key‑value store (e.g., Redis) and expose them as subscriber metadata. Novu can read this metadata directly in the workflow condition, eliminating extra DB calls.
2. Incident Alerts for DevOps Teams
When a CI/CD pipeline fails, the right people need to be notified instantly on Slack, PagerDuty, and via email. Novu’s webhook provider makes it trivial to integrate with any internal monitoring tool.
First, create a webhook provider that points to your CI system’s event endpoint.
webhook_provider = novu.providers.create(
name="ci-webhook",
channel="webhook",
credentials={"url": "https://ci.example.com/events"}
)
Then, craft a workflow that routes the alert based on severity.
alert_workflow = novu.workflows.create(
name="ci-failure-alert",
steps=[
{
"type": "conditional",
"condition": "{{ event.severity }} == 'high'",
"true_steps": [
{"type": "notification", "template_id": "slack-high-severity"},
{"type": "notification", "template_id": "pagerduty-high"}
],
"false_steps": [
{"type": "notification", "template_id": "slack-low-severity"}
]
}
],
active=True,
)
Now, whenever your CI system posts a JSON payload to the webhook, Novu evaluates the severity and dispatches the appropriate alerts without any custom code on the CI side.
Note: Enable deduplication in the workflow settings to avoid spamming the same alert multiple times within a short window.
3. Personalized In‑App Campaigns
Product teams love A/B testing messaging inside the app. Novu’s in‑app provider supports rich UI components and can be toggled per user segment.
Suppose you want to show a “Upgrade to Pro” banner only to users who have logged in more than 10 times but haven’t purchased yet.
campaign_workflow = novu.workflows.create(
name="pro-upgrade-campaign",
steps=[
{
"type": "conditional",
"condition": "{{ subscriber.login_count }} > 10 and not {{ subscriber.is_pro }}",
"true_steps": [
{"type": "notification", "template_id": "inapp-pro-banner"}
],
"false_steps": []
}
],
active=True,
)
The inapp-pro-banner template can embed a React component or plain HTML, and Novu will render it directly in the client SDK.
Deep Dive: Extending Novu with Custom Providers
While Novu ships with over 30 providers, you might need to integrate a niche SMS gateway or an internal messaging bus. The SDK lets you register a custom provider by implementing two methods: send and validate.
from novu.providers.base import BaseProvider
class MyCustomSMSProvider(BaseProvider):
def __init__(self, api_key: str):
self.api_key = api_key
def validate(self) -> bool:
# Simple health check against the provider's status endpoint
response = requests.get("https://api.mysms.com/status", headers={"Authorization": f"Bearer {self.api_key}"})
return response.status_code == 200
def send(self, payload: dict) -> dict:
# Payload contains: to, content, etc.
response = requests.post(
"https://api.mysms.com/send",
json=payload,
headers={"Authorization": f"Bearer {self.api_key}"}
)
return response.json()
Register the provider with Novu:
novu.providers.register(
name="my-custom-sms",
provider_class=MyCustomSMSProvider,
config={"api_key": "MY_CUSTOM_SMS_API_KEY"}
)
After registration, you can use my-custom-sms just like any built‑in provider in your workflows.
Pro tip: Wrap your custom provider in a retry decorator that respects exponential backoff. Novu will honor the retry flag you set on the workflow step, but handling transient errors at the provider level yields cleaner logs.
Best Practices for Scaling Notification Pipelines
- Separate Templates from Code: Keep all notification copy in Novu’s UI or as JSON files in version control. This allows copywriters to iterate without a deployment.
- Leverage Subscriber Metadata: Store preferences, language, and device tokens as metadata. It reduces DB lookups during workflow execution.
- Use Idempotent Payloads: Include a unique
event_idin each trigger. Novu can deduplicate based on this ID, preventing duplicate emails on retries. - Monitor Delivery Metrics: Novu emits webhook events for
delivered,failed, andopened. Pipe these into your observability stack (e.g., Grafana) to spot delivery issues early. - Chunk Large Broadcasts: For newsletters or system‑wide alerts, break the audience into batches (e.g., 10k recipients per batch) to stay within provider rate limits.
Testing Notifications Locally
During development you don’t want to spam real users. Novu provides a “development” environment where you can route all notifications to a sandbox email address or a local webhook.
novu = Novu(
api_key="DEV_API_KEY",
environment="development" # Sends to dev inbox / mock endpoints
)
# Trigger a test notification
novu.notifications.trigger(
subscriber_id="dev_user",
template_id="welcome-email",
payload={"user": {"name": "Dev Tester"}}
)
Alternatively, spin up the open‑source Novu server locally with Docker:
docker run -p 3000:3000 -e DATABASE_URL=postgres://... novu/novu:latest
Connect your SDK to http://localhost:3000 and you have a fully functional sandbox.
Security Considerations
Notifications often contain sensitive data—reset tokens, account numbers, or private messages. Follow these guidelines to keep them safe:
- Encrypt Provider Credentials: Store API keys in a secret manager (AWS Secrets Manager, Vault) and inject them at runtime.
- Enable TLS Everywhere: Novu enforces HTTPS for all external provider calls. Verify that your self‑hosted instance also runs behind TLS.
- Restrict Webhook Endpoints: Use HMAC signatures to verify incoming webhook payloads, preventing spoofed events.
- Audit Logs: Enable Novu’s audit trail feature to track who created/modified templates and workflows.
Performance Optimizations
When you’re sending millions of notifications per day, latency and throughput become critical. Here are a few knobs you can turn:
- Batch Provider Calls: For email, enable SMTP pooling; for push, use FCM’s batch API.
- Asynchronous Workers: Deploy Novu’s worker service with a high concurrency setting (e.g., Celery workers with 50+ processes).
- Cache Subscriber Lookups: Cache the subscriber metadata for 5‑10 minutes to avoid repetitive DB reads.
- Rate‑Limit per Provider: Configure per‑provider throttling in Novu to stay within vendor limits and avoid 429 errors.
Pro tip: Enable event streaming (Kafka or Redis Streams) in Novu’s config. This decouples notification generation from delivery, allowing you to scale workers independently of the API layer.
Monitoring & Observability
Novu emits a rich set of events that you can forward to your monitoring stack. Here’s a quick example of piping events to a Prometheus exporter:
from novu.webhooks import WebhookServer
from prometheus_client import Counter, start_http_server
sent_counter = Counter('novu_notifications_sent', 'Number of notifications sent', ['provider'])
failed_counter = Counter('novu_notifications_failed', 'Number of failed notifications', ['provider'])
def handle_event(event):
if event['type'] == 'notification.sent':
sent_counter.labels(provider=event['provider']).inc()
elif event['type'] == 'notification.failed':
failed_counter.labels(provider=event['provider']).inc()
webhook = WebhookServer(secret="WEBHOOK_SECRET")
webhook.register_handler(handle_event)
start_http_server(8000) # Expose /metrics for Prometheus
webhook.start()
With these metrics, you can set alerts for spikes in failures, latency, or provider‑specific issues.
Community & Contribution
Novu’s GitHub repository is vibrant, with over 5k stars and a growing ecosystem of plugins. If you encounter a missing provider, consider opening a PR – the contribution guidelines are straightforward, and the maintainers review within 48 hours.
Beyond code, the community maintains a Discord channel where you can ask for architectural advice, share templates, or showcase your custom provider. Engaging with the community not only speeds up problem solving but also puts you on the radar for early access to new features.
Conclusion
Novu transforms notification management from a tangled mess of API keys and retry loops into a clean, declarative workflow engine. By centralizing templates, abstracting providers, and offering a robust SDK, it empowers developers to ship reliable, multi‑channel communication features faster.
Whether you’re a solo founder building an MVP, a mid‑size startup scaling to millions of users, or an enterprise team needing strict compliance, Novu’s open‑source core and extensible architecture fit the bill. Start with the minimal example, iterate on workflows, and gradually unlock advanced capabilities like custom providers, event streaming, and granular observability.
Remember: the best notifications are those that arrive at the right moment, on the right channel, and with the right message. Novu gives you the toolbox to make that happen—so you can focus on building the product that users love.