Skip to content

rmzlb/notifyd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

26 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

notifyd

notifyd

Agent-first notification service. One Rust binary. Postgres only. No Redis, no Mongo, no nonsense.

License Docker Rust Lines of code

Quick Start β€’ API Reference β€’ Setup Guide β€’ Architecture β€’ LLM Docs β€’ Contributing


The Problem

Your AI agent needs to send an email. Or a push notification. Or update an in-app inbox.

You look at Novu: MongoDB, Redis, 4 containers, a React SDK, 30 minutes of setup. Your agent doesn't care about any of that. It just wants to POST /v1/send and move on.

notifyd is what that looks like. A single Rust binary. One POST call. Your agent sends notifications and gets back to work.

Agent ──POST /v1/send──→ notifyd ──→ Email (Resend)
                                 ──→ SMS (Twilio/Telnyx)
                                 ──→ Push (FCM)
                                 ──→ In-App (SSE)

Why Agents Love This

Most notification services were designed for humans clicking buttons in a dashboard. notifyd was designed for agents making API calls.

Flat REST API β€” no SDK needed, no WebSocket handshake, no complex auth flows. curl works. Your agent's HTTP client works.

docs/llms.txt β€” the entire API reference in plain text, optimized for LLM context windows. Point your agent at it and it can call any endpoint. (View it)

Idempotency built-in β€” agents retry. That's fine. Pass idempotency_key and notifyd deduplicates.

One binary, one config file β€” docker compose up and you have a notification service. No infra degree required.

# Your agent sends a notification. That's it.
curl -X POST http://localhost:3400/v1/send \
  -H "X-Api-Key: sk_myapp_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "channels": ["email", "in_app"],
    "subscriber_id": "user-1",
    "subject": "Your report is ready",
    "body": "Hey {{first_name}}, the analysis you requested is complete.",
    "vars": {"first_name": "Alice"},
    "idempotency_key": "report-42-ready"
  }'

Connect Your Agent to the Docs

Feed docs/llms.txt to any LLM agent and it can operate the full API:

https://raw.githubusercontent.com/rmzlb/notifyd/main/docs/llms.txt

Or describe notifyd as a tool:

{
  "name": "send_notification",
  "description": "Send email/SMS/push/in-app via notifyd",
  "endpoint": "POST /v1/send",
  "auth": "X-Api-Key header"
}

vs. The Alternatives

Novu Knock notifyd
Infra MongoDB + Redis + 4 containers Hosted SaaS Postgres only
Setup 30+ min Signup + dashboard docker compose up (2 min)
Language Node.js (multiple services) N/A (hosted) Rust (single binary)
Memory ~800MB+ N/A ~15MB
Agent-friendly SDK-heavy Dashboard-first REST-first, llms.txt included
Realtime WebSocket WebSocket SSE (simpler, works everywhere)
Self-hosted βœ… (heavy) ❌ βœ… (one container)
Queue Redis + BullMQ Managed Postgres SKIP LOCKED
Cost Free tier / paid $0.01/notification Free forever

Features

  • πŸ“§ Email β€” via Resend (plug your API key)
  • πŸ“± SMS β€” Twilio or Telnyx (swap in config, zero code change)
  • πŸ”” Push β€” FCM (Firebase Cloud Messaging)
  • πŸ’¬ In-app inbox β€” REST + realtime SSE stream
  • ⏰ Scheduling β€” scheduled_at on any notification
  • πŸ”„ Retry β€” exponential backoff (30s β†’ 2min β†’ 10min)
  • πŸ”‘ Idempotency β€” safe agent retries
  • πŸ“‹ Templates β€” {{variable}} substitution, stored per project
  • 🏒 Multi-project β€” one instance, many projects, isolated by API key
  • ⚑ Workflows β€” event-triggered multi-step sequences
  • πŸ‘€ Preferences β€” per-subscriber opt-in/opt-out
  • πŸ” Audit log β€” every mutation logged
  • 🚦 Rate limiting β€” per-project sliding window
  • πŸ“Š Metrics β€” /v1/metrics for monitoring
  • πŸͺ Webhooks β€” delivery events to your endpoints

Quick Start

Docker (recommended)

git clone https://github.com/rmzlb/notifyd.git && cd notifyd
cp notifyd.toml.example notifyd.toml
# Edit notifyd.toml β€” add your Resend API key at minimum

docker compose up -d
# β†’ notifyd running on http://localhost:3400

From source

# Rust 1.75+, PostgreSQL 16+
git clone https://github.com/rmzlb/notifyd.git && cd notifyd
cp notifyd.toml.example notifyd.toml
cargo run

Verify

curl http://localhost:3400/v1/health
# β†’ {"status":"ok","db":"ok","version":"0.1.0"}

β†’ Full setup: docs/SETUP.md


API at a Glance

Every endpoint uses X-Api-Key: sk_<project>_xxx. Inbox endpoints also accept subscriber JWT.

Method Endpoint What it does
POST /v1/send Send notification (email, SMS, push, in-app)
POST /v1/batch Send to multiple subscribers
GET /v1/inbox/:id List in-app notifications
GET /v1/inbox/:id/stream SSE realtime stream
POST /v1/workflows/trigger Trigger event-based workflow
GET /v1/health Health check
GET /v1/metrics Service metrics

β†’ Full reference: docs/API.md β€” or feed docs/llms.txt to your agent.


TypeScript SDK

A small official SDK now ships in this repo for backend + frontend apps.

pnpm add notifyd-sdk@github:rmzlb/notifyd
import { createNotifydClient } from 'notifyd-sdk';

const notifyd = createNotifydClient({
  url: process.env.NOTIFYD_URL!,
  apiKey: process.env.NOTIFYD_API_KEY!,
});

await notifyd.send({
  channels: ['email', 'in_app'],
  subscriberId: 'user-123',
  subject: 'Your report is ready',
  body: 'Hey {{first_name}}, the analysis is complete.',
  vars: { first_name: 'Alice' },
});

const token = await notifyd.createSubscriberToken({
  subscriberId: 'user-123',
  ttlHours: 8,
});

It wraps the REST API with typed helpers for send, subscribers, inbox, unread count, mark read, and SSE stream setup.


In-App Inbox

Complete notification inbox with realtime SSE. No WebSocket library, no Redis pub/sub β€” just native EventSource.

// Connect to realtime stream
const events = new EventSource(
  `https://notifyd.example.com/v1/inbox/${userId}/stream?token=${jwt}`
);

events.onmessage = (e) => {
  const data = JSON.parse(e.data);
  if (data.type === 'new_notification') showToast(data.notification);
  if (data.type === 'count_update') updateBadge(data.unread_count);
};

Features: read/unread, archive, todo/star, pagination, unread count badge, realtime push.


Workflow Engine

Multi-step notification sequences triggered by events:

curl -X POST http://localhost:3400/v1/workflows \
  -H "X-Api-Key: sk_myapp_xxx" \
  -d '{
    "id": "welcome-series",
    "trigger_event": "user.signup",
    "steps": [
      {"type": "send", "channel": "email", "template": "welcome"},
      {"type": "delay", "duration": "24h"},
      {"type": "send", "channel": "email", "template": "getting_started"},
      {"type": "delay", "duration": "72h"},
      {"type": "condition", "check": "completed_onboarding", "if_false": [
        {"type": "send", "channel": "email", "template": "nudge"}
      ]}
    ]
  }'

State persisted in Postgres β€” survives restarts. No in-memory state to lose.


Configuration

Single TOML file. Minimal setup:

[server]
port = 3400
jwt_secret = "your-secret-here"

[database]
url = "postgres://notifyd:pass@localhost:5432/notifyd"

[connectors.email]
provider = "resend"
api_key = "re_xxx"
from = "notifications@yourdomain.com"

[projects.myapp]
api_key = "sk_myapp_xxx"
channels = ["email", "in_app"]

β†’ Full config: notifyd.toml.example


Project Structure

notifyd/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ main.rs              # Server bootstrap, graceful shutdown
β”‚   β”œβ”€β”€ config.rs             # TOML config
β”‚   β”œβ”€β”€ db.rs                 # sqlx models
β”‚   β”œβ”€β”€ worker.rs             # Background job processor
β”‚   β”œβ”€β”€ workflow_engine.rs    # Event-driven workflows
β”‚   β”œβ”€β”€ sse.rs                # SSE broadcaster (tokio channels)
β”‚   β”œβ”€β”€ templates.rs          # {{var}} engine
β”‚   β”œβ”€β”€ pii.rs                # PII masking for logs
β”‚   β”œβ”€β”€ middleware.rs          # Rate limiter + audit
β”‚   β”œβ”€β”€ api/                  # 13 route modules
β”‚   └── connectors/           # Email, SMS, Push, In-App
β”œβ”€β”€ migrations/               # 4 SQL migrations (auto-run)
β”œβ”€β”€ Dockerfile                # Multi-stage + cargo-chef
β”œβ”€β”€ docker-compose.yml        # notifyd + Postgres
β”œβ”€β”€ notifyd.toml.example      # Config reference
└── docs/                     # API ref, setup, architecture, llms.txt

~3,700 lines of Rust. Every file < 400 lines.


Documentation

πŸ“¦ Setup Guide Local dev, Docker, production deploy
πŸ”Œ API Reference Every endpoint with curl/TS/Rust examples
πŸ—οΈ Architecture Queue design, SSE internals, connectors
πŸ€– LLM Docs Full API in plain text β€” feed to your agent

Contributing

notifyd is built in Grenoble, in the French Alps πŸ”οΈ β€” but open to contributors from everywhere.

  1. Read the Contributing Guide
  2. Check open issues β€” good first issue is a great start
  3. Big features β†’ open an issue first
git clone https://github.com/YOUR_USERNAME/notifyd.git
cd notifyd && cp notifyd.toml.example notifyd.toml
cargo test && cargo run

License

MIT β€” use it however you want.


Built with πŸ¦€ in Grenoble, France πŸ”οΈ

About

Self-hosted notification micro-service in Rust. Email, SMS, in-app (SSE), push. Replaces Novu.

Resources

License

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors